From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / glx / lament.c
1 /* xscreensaver, Copyright (c) 1998-2018 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* Animates Lemarchand's Box, the Lament Configuration.  By jwz, 25-Jul-98.
13
14    TODO:
15
16      *  The gold leaf should appear to be raised up from the surface, but
17         I think this isn't possible with OpenGL.  No bump maps.
18
19      *  There should be strange lighting effects playing across the surface:
20         electric sparks, or little glittery blobs of light.  Maybe like
21         http://www.opengl.org/archives/resources/features/KilgardTechniques/
22         LensFlare/
23
24      *  Chains.
25
26      *  Needs music.  ("Hellraiser Themes" by Coil: TORSO CD161; also
27         duplicated on the "Unnatural History 2" compilation, WORLN M04699.)
28  */
29
30 #define DEFAULTS        "*delay:        20000   \n"     \
31                         "*showFPS:      False   \n"     \
32                         "*wireframe:    False   \n"     \
33                         "*suppressRotationAnimation: True\n" \
34
35 # define free_lament 0
36 # define release_lament 0
37 #include "xlockmore.h"
38
39 #ifdef USE_GL /* whole file */
40
41 #include "gllist.h"
42
43 /* #define DEBUG_MODE LAMENT_LEVIATHAN_COLLAPSE */
44
45 #undef countof
46 #define countof(x) (sizeof((x))/sizeof((*x)))
47 #undef MAX
48 #define MAX(x, y) ((x) > (y) ? (x) : (y))
49 #undef MIN
50 #define MIN(x, y) ((x) < (y) ? (x) : (y))
51
52 extern const struct gllist
53   *lament_model_box,
54   *lament_model_iso_base_a,
55   *lament_model_iso_base_b,
56   *lament_model_iso_den,
57   *lament_model_iso_dse,
58   *lament_model_iso_dwn,
59   *lament_model_iso_swd,
60   *lament_model_iso_une,
61   *lament_model_iso_unw,
62   *lament_model_iso_use,
63   *lament_model_iso_usw,
64   *lament_model_leviathan,
65   *lament_model_lid_a,
66   *lament_model_lid_b,
67   *lament_model_lid_base,
68   *lament_model_lid_c,
69   *lament_model_lid_d,
70   *lament_model_pillar_a,
71   *lament_model_pillar_b,
72   *lament_model_pillar_base,
73   *lament_model_star_d,
74   *lament_model_star_u,
75   *lament_model_taser_a,
76   *lament_model_taser_b,
77   *lament_model_taser_base,
78   *lament_model_tetra_base,
79   *lament_model_tetra_dse,
80   *lament_model_tetra_dwn,
81   *lament_model_tetra_une,
82   *lament_model_tetra_usw;
83
84 static const struct gllist * const *all_objs[] = {
85   &lament_model_box,
86   &lament_model_iso_base_a,
87   &lament_model_iso_base_b,
88   &lament_model_iso_den,
89   &lament_model_iso_dse,
90   &lament_model_iso_dwn,
91   &lament_model_iso_swd,
92   &lament_model_iso_une,
93   &lament_model_iso_unw,
94   &lament_model_iso_use,
95   &lament_model_iso_usw,
96   &lament_model_leviathan,
97   &lament_model_lid_a,
98   &lament_model_lid_b,
99   &lament_model_lid_base,
100   &lament_model_lid_c,
101   &lament_model_lid_d,
102   &lament_model_pillar_a,
103   &lament_model_pillar_b,
104   &lament_model_pillar_base,
105   &lament_model_star_d,
106   &lament_model_star_u,
107   &lament_model_taser_a,
108   &lament_model_taser_b,
109   &lament_model_taser_base,
110   &lament_model_tetra_base,
111   &lament_model_tetra_dse,
112   &lament_model_tetra_dwn,
113   &lament_model_tetra_une,
114   &lament_model_tetra_usw
115 };
116
117 typedef enum {  /* must be in the same order as in `all_objs'. */
118   OBJ_BOX = 0,
119   OBJ_ISO_BASE_A,
120   OBJ_ISO_BASE_B,
121   OBJ_ISO_DEN,
122   OBJ_ISO_DSE,
123   OBJ_ISO_DWN,
124   OBJ_ISO_SWD,
125   OBJ_ISO_UNE,
126   OBJ_ISO_UNW,
127   OBJ_ISO_USE,
128   OBJ_ISO_USW,
129   OBJ_LEVIATHAN,
130   OBJ_LID_A,
131   OBJ_LID_B,
132   OBJ_LID_BASE,
133   OBJ_LID_C,
134   OBJ_LID_D,
135   OBJ_PILLAR_A,
136   OBJ_PILLAR_B,
137   OBJ_PILLAR_BASE,
138   OBJ_STAR_D,
139   OBJ_STAR_U,
140   OBJ_TASER_A,
141   OBJ_TASER_B,
142   OBJ_TASER_BASE,
143   OBJ_TETRA_BASE,
144   OBJ_TETRA_DSE,
145   OBJ_TETRA_DWN,
146   OBJ_TETRA_UNE,
147   OBJ_TETRA_USW
148 } lament_obj_index;
149
150
151 #define DEF_TEXTURE "True"
152
153 static int do_texture;
154
155 static XrmOptionDescRec opts[] = {
156   {"-texture", ".lament.texture", XrmoptionNoArg, "true" },
157   {"+texture", ".lament.texture", XrmoptionNoArg, "false" },
158 };
159
160 static argtype vars[] = {
161   {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
162 };
163
164 ENTRYPOINT ModeSpecOpt lament_opts = {countof(opts), opts, countof(vars), vars, NULL};
165
166 #include "ximage-loader.h"
167 #include "rotator.h"
168 #include "gltrackball.h"
169 #include "normals.h"
170
171 #include "images/gen/lament512_png.h"
172
173 #define RANDSIGN() ((random() & 1) ? 1 : -1)
174
175 typedef enum {
176   LAMENT_BOX,
177
178   LAMENT_STAR_OUT,
179   LAMENT_STAR_ROT,
180   LAMENT_STAR_ROT_IN,
181   LAMENT_STAR_ROT_OUT,
182   LAMENT_STAR_UNROT,
183   LAMENT_STAR_IN,
184
185   LAMENT_TETRA_UNE,
186   LAMENT_TETRA_USW,
187   LAMENT_TETRA_DWN,
188   LAMENT_TETRA_DSE,
189
190   LAMENT_LID_OPEN,
191   LAMENT_LID_CLOSE,
192   LAMENT_LID_ZOOM,
193
194   LAMENT_TASER_OUT,
195   LAMENT_TASER_SLIDE,
196   LAMENT_TASER_SLIDE_IN,
197   LAMENT_TASER_IN,
198
199   LAMENT_PILLAR_OUT,
200   LAMENT_PILLAR_SPIN,
201   LAMENT_PILLAR_IN,
202
203   LAMENT_SPHERE_OUT,
204   LAMENT_SPHERE_IN,
205
206   LAMENT_LEVIATHAN_SPIN,
207   LAMENT_LEVIATHAN_FADE,
208   LAMENT_LEVIATHAN_TWIST,
209   LAMENT_LEVIATHAN_COLLAPSE,
210   LAMENT_LEVIATHAN_EXPAND,
211   LAMENT_LEVIATHAN_UNTWIST,
212   LAMENT_LEVIATHAN_UNFADE,
213   LAMENT_LEVIATHAN_UNSPIN,
214
215 } lament_type;
216
217 static const GLfloat exterior_color[] =
218  { 0.33, 0.22, 0.03, 1.00,  /* ambient    */
219    0.78, 0.57, 0.11, 1.00,  /* specular   */
220    0.99, 0.91, 0.81, 1.00,  /* diffuse    */
221    27.80                    /* shininess  */
222  };
223 static const GLfloat interior_color[] =
224  { 0.20, 0.20, 0.15, 1.00,  /* ambient    */
225    0.40, 0.40, 0.32, 1.00,  /* specular   */
226    0.99, 0.99, 0.81, 1.00,  /* diffuse    */
227    50.80                    /* shininess  */
228  };
229 static const GLfloat leviathan_color[] =
230  { 0.30, 0.30, 0.30, 1.00,  /* ambient    */
231    0.85, 0.85, 0.95, 1.00,  /* specular   */
232    0.99, 0.99, 0.99, 1.00,  /* diffuse    */
233    50.80                    /* shininess  */
234  };
235 static const GLfloat black_color[] =
236  { 0.05, 0.05, 0.05, 1.00,  /* ambient    */
237    0.05, 0.05, 0.05, 1.00,  /* specular   */
238    0.05, 0.05, 0.05, 1.00,  /* diffuse    */
239    80.00                    /* shininess  */
240  };
241
242
243 typedef struct {
244   GLXContext *glx_context;
245   rotator *rot;
246   double rotx, roty, rotz;
247   trackball_state *trackball;
248   Bool button_down_p;
249   Bool ffwdp;
250
251   GLuint dlists[countof(all_objs)];
252   GLuint polys[countof(all_objs)];
253
254   XImage *texture;                 /* image bits */
255   GLuint texids[8];                /* texture map IDs */
256   lament_type type;                /* which mode of the object is current */
257
258   int anim_pause;                  /* countdown before animating again */
259   GLfloat anim_r, anim_y, anim_z;  /* relative position during anims */
260   Bool facing_p;
261
262   int state, nstates;
263   lament_type *states;
264
265 } lament_configuration;
266
267 static lament_configuration *lcs = NULL;
268
269
270 static Bool
271 facing_screen_p (ModeInfo *mi)
272 {
273   Bool facing_p;
274   GLdouble m[16], p[16], x, y, z;
275   GLint v[4];
276   glGetDoublev (GL_MODELVIEW_MATRIX, m);
277   glGetDoublev (GL_PROJECTION_MATRIX, p);
278   glGetIntegerv (GL_VIEWPORT, v);
279         
280   /* See if a coordinate 5 units in front of the door is near the
281      center of the screen. */
282   gluProject (0, -5, 0, m, p, v, &x, &y, &z);
283   x = (x / MI_WIDTH(mi))  - 0.5;
284   y = (y / MI_HEIGHT(mi)) - 0.5;
285
286   facing_p = (z < 0.9 &&
287               x > -0.15 && x < 0.15 &&
288               y > -0.15 && y < 0.15);
289
290 # ifdef DEBUG_MODE
291   glBindTexture(GL_TEXTURE_2D, 0);
292   glDisable (GL_LIGHTING);
293   glColor3f (1, (facing_p ? 1 : 0), 0);
294   glBegin (GL_LINES);
295   glVertex3f (0, 0, 0);
296   glVertex3f (0, -5, 0);
297   glEnd();
298   if (!MI_IS_WIREFRAME(mi)) glEnable (GL_LIGHTING);
299 # endif /* DEBUG_MODE */
300
301   return facing_p;
302 }
303
304
305 static void
306 scale_for_window (ModeInfo *mi)
307 {
308   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
309
310   GLfloat target_size = 1.4 * (lc->texture ? lc->texture->width : 512);
311   GLfloat size = MI_WIDTH(mi) < MI_HEIGHT(mi) ? MI_WIDTH(mi) : MI_HEIGHT(mi);
312   GLfloat scale;
313
314   /* Make it take up roughly the full width of the window. */
315   scale = 20;
316
317   /* But if the window is wider than tall, make it only take up the
318      height of the window instead.
319    */
320   if (MI_WIDTH(mi) > MI_HEIGHT(mi))
321     scale /= MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
322
323   /* If the window is super wide, make it bigger. */
324   if (scale < 8) scale = 8;
325
326   /* Constrain it to roughly life-sized on the screen, not huge.
327    */
328 # ifdef HAVE_MOBILE
329   if (size > 768)  /* iPad retina / iPhone 6 */
330     target_size *= 1.5;
331   else
332 # endif
333     {
334       GLfloat max = 500;  /* 3" on my screen... */
335
336       if (MI_WIDTH(mi) > 2560) {  /* Retina displays */
337         target_size *= 2.5;
338         max *= 2.5;
339       }
340
341       if (target_size > max)
342         target_size = max;
343     }
344
345   /* But if that would make the image larger than target_size, scale it
346      back down again.  The image-map bits we have are 512x512, so if the
347      image is magnified a lot, it looks pretty blocky.  It's better to
348      have a 512x512 animation on a 1920x1080 screen that looks good
349      than a 1024x1024 animation that looks really pixelated.
350    */
351   if (size > target_size)
352     scale *= target_size / size;
353
354   glScalef (scale, scale, scale);
355 }
356
357
358 static void
359 set_colors (const GLfloat *color)
360 {
361   glMaterialfv(GL_FRONT, GL_AMBIENT,   color + 0);
362   glMaterialfv(GL_FRONT, GL_DIFFUSE,   color + 4);
363   glMaterialfv(GL_FRONT, GL_SPECULAR,  color + 8);
364   glMaterialfv(GL_FRONT, GL_SHININESS, color + 12);
365 }
366
367 static void
368 set_colors_alpha (const GLfloat *color, GLfloat a)
369 {
370   GLfloat c[countof(leviathan_color)];
371   memcpy (c, color, sizeof(c));
372   c[3] = c[7] = c[11] = a;
373   set_colors (c);
374 }
375
376
377 static void
378 which_face (ModeInfo *mi, const GLfloat *f, int *face, int *outerp)
379 {
380   GLfloat size = 3;          /* 3" square */
381   const GLfloat *n = f;      /* normal */
382   const GLfloat *v = f + 3;  /* vertex */
383   GLfloat slack = 0.01;
384
385   /* First look at the normal to determine which direction this triangle
386      is facing (or is most-closely facing).
387      It's an outer surface if it is within epsilon of the cube wall that
388      it is facing.  Otherwise, it's an inner surface.
389    */
390   if      (n[1] < -0.5)   *face = 1, *outerp = v[1] < slack;       /* S */
391   else if (n[2] >  0.5)   *face = 2, *outerp = v[2] > size-slack;  /* U */
392   else if (n[1] >  0.5)   *face = 3, *outerp = v[1] > size-slack;  /* N */
393   else if (n[2] < -0.5)   *face = 4, *outerp = v[2] < slack;       /* D */
394   else if (n[0] < -0.5)   *face = 5, *outerp = v[0] < slack;       /* W */
395   else /* (n[0] >  0.5)*/ *face = 6, *outerp = v[0] > size-slack;  /* E */
396
397   /* Faces that don't have normals parallel to the axes aren't external. */
398   if (*outerp &&
399       (n[0] > -0.95 && n[0] < 0.95 &&
400        n[1] > -0.95 && n[1] < 0.95 &&
401        n[2] > -0.95 && n[2] < 0.95))
402     *outerp = 0;
403 }
404
405
406 static void
407 texturize_vert (ModeInfo *mi, int which, const GLfloat *v)
408 {
409   GLfloat size = 3;          /* 3" square */
410   GLfloat s = 0, q = 0;
411
412   /* Texture coordinates are surface coordinates,
413      on the plane of this cube wall. */
414   switch (which) {
415   case 0: break;
416   case 1: s = v[0], q = v[2]; break;
417   case 2: s = v[0], q = v[1]; break;
418   case 3: s = v[0], q = v[2]; q = size - q; break;
419   case 4: s = v[0], q = v[1]; q = size - q; break;
420   case 5: s = v[1], q = v[2]; break;
421   case 6: s = v[1], q = v[2]; break;
422   default: abort(); break;
423   }
424
425   glTexCoord2f (s / size, q / size);
426 }
427
428
429 static void
430 leviathan (ModeInfo *mi, GLfloat ratio, GLfloat alpha, Bool top_p)
431 {
432   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
433   Bool wire = MI_IS_WIREFRAME(mi);
434   GLfloat r = 0.34;
435   GLfloat z = 2 * ratio;
436   XYZ p[3];
437   int i;
438
439   GLfloat th = acos (2 / sqrt (6));  /* Line up with cube's diagonal */
440
441   glPushMatrix();
442
443   glRotatef (-45, 0, 1, 0);
444   glRotatef (-th * 180 / M_PI, 0, 0, 1);
445
446   if (!top_p)
447     glRotatef (180, 0, 0, 1);
448
449   for (i = 0; i < countof(p); i++)
450     {
451       GLfloat th = i * M_PI * 2 / countof(p);
452       p[i].x = cos(th) * r;
453       p[i].y = sin(th) * r;
454     }
455
456   glFrontFace (GL_CCW);
457   for (i = 0; i < countof(p); i++)
458     {
459       int j = (i + 1) % countof(p);
460 /*      if (top_p)*/
461         do_normal (z, 0, 0,
462                    0, p[i].x, p[i].y,
463                    0, p[j].x, p[j].y);
464 /*
465       else
466         do_normal (z, 0, 0,
467                    0, p[j].y, p[j].z,
468                    0, p[i].y, p[i].z);
469 */
470
471       if (do_texture)  /* Leviathan is the final texture */
472         glBindTexture (GL_TEXTURE_2D, lc->texids[countof(lc->texids) - 1]);
473
474       set_colors (leviathan_color);
475
476       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
477       glTexCoord2f (0.5, 1);
478       glVertex3f (z, 0, 0);
479
480       glTexCoord2f (0, 0);
481       glVertex3f (0, p[i].x, p[i].y);
482
483       glTexCoord2f (1, 0);
484       glVertex3f (0, p[j].x, p[j].y);
485       glEnd();
486       mi->polygon_count++;
487
488       /* Shield for fading */
489       if (alpha < 0.9 && !wire)
490         {
491           GLfloat a = 0.35;
492           GLfloat b = 0.69;
493
494           set_colors_alpha (black_color, 1-alpha);
495           glBindTexture (GL_TEXTURE_2D, 0);
496           if (!wire)
497             {
498               glEnable (GL_BLEND);
499               glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
500             }
501
502           glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
503
504           glVertex3f (z*a, p[j].x * b, p[j].y * b);
505           glVertex3f (z*a, p[i].x * b, p[i].y * b);
506           glVertex3f (0, p[i].x * 1.01, p[i].y * 1.01);
507           glVertex3f (0, p[j].x * 1.01, p[j].y * 1.01);
508           glEnd();
509           mi->polygon_count++;
510           glDisable (GL_BLEND);
511         }
512     }
513
514   glPopMatrix();
515 }
516
517
518 static void
519 folding_walls (ModeInfo *mi, GLfloat ratio, Bool top_p)
520 {
521   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
522   Bool wire = MI_IS_WIREFRAME(mi);
523   const GLfloat pa[4][2] = {{ -0.5,      -0.215833 },
524                             {  0,         0.5      },
525                             {  0.5,       0        },
526                             { -0.215833, -0.5      }};
527   const int tex[6] = { 0, 5, 1,  4, 2, 3 };
528   const GLfloat top = -pa[0][1];
529   GLfloat end_angle = 30.85;
530   GLfloat rr = sin (ratio / 2 * M_PI);
531   GLfloat offa = 0.15 * rr;
532   GLfloat offb = 0.06 * rr;
533   GLfloat p[4][3];
534   GLfloat t[4][2];
535   int i;
536
537   glPushMatrix();
538
539   if (top_p)
540     {
541       glRotatef (60, 1, -1, 1);
542       glRotatef (180, 0, 1, 0);
543       glRotatef (90, 1, 0, 0);
544     }
545   else
546     {
547       glRotatef (180, 1, 0, 0);
548     }
549
550   /* Scale down the points near the axis */
551
552   p[0][0] = pa[0][0];
553   p[0][1] = 0.5;
554   p[0][2] = pa[0][1];
555
556   p[1][0] = pa[1][0] - offb;
557   p[1][1] = 0.5;
558   p[1][2] = pa[1][1] - offa;
559
560   p[2][0] = pa[2][0] - offa;
561   p[2][1] = 0.5;
562   p[2][2] = pa[2][1] - offb;
563
564   p[3][0] = pa[3][0];
565   p[3][1] = 0.5;
566   p[3][2] = pa[3][1];
567
568   if (!wire)
569     {
570       glEnable (GL_BLEND);
571       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
572     }
573
574   for (i = 0; i < 3; i++)
575     {
576       glPushMatrix();
577
578       if (i == 1)
579         {
580           glRotatef (-90, 1, 0, 0);
581           glRotatef (180, 1, 1, 0);
582         }
583       else if (i == 2)
584         {
585           glRotatef (-90, 1, 0, 0);
586           glRotatef (180, 0, 1, 0);
587           glRotatef (90, 0, 1, 0);
588         }
589
590       glRotatef (-90, 0, 1, 0);
591
592       glTranslatef (-(top/2 + 0.25), 0.5, -(top/2 + 0.25));
593       glRotatef (-45, 0, 1, 0);
594       glRotatef (ratio * -end_angle, 0, 0, 1);
595       glRotatef (45, 0, 1, 0);
596       glTranslatef (top/2 + 0.25, -0.5, top/2 + 0.25);
597
598       /* Get the texture coordinates right.
599          This is hairy and incomprehensible. */
600
601       t[0][0] = pa[0][1] + 0.5; t[0][1] = pa[0][0] + 0.5;
602       t[1][0] = pa[1][1] + 0.5; t[1][1] = pa[1][0] + 0.5;
603       t[2][0] = pa[2][1] + 0.5; t[2][1] = pa[2][0] + 0.5;
604       t[3][0] = pa[3][1] + 0.5; t[3][1] = pa[3][0] + 0.5;
605
606       if (i == 0 && !top_p)
607         {
608 # define SWAP(A,B) A = 1-A, B = 1-B
609           SWAP(t[0][0], t[0][1]);
610           SWAP(t[1][0], t[1][1]);
611           SWAP(t[2][0], t[2][1]);
612           SWAP(t[3][0], t[3][1]);
613 # undef SWAP
614         }
615       else if (i == 0 && top_p)
616         {
617           GLfloat ot[4][3];
618           memcpy (ot, t, sizeof(t));
619 # define SWAP(A,B) A = 1-A, B = 1-B
620           SWAP(t[0][0], ot[2][1]);
621           SWAP(t[1][0], ot[3][1]);
622           SWAP(t[2][0], ot[0][1]);
623           SWAP(t[3][0], ot[1][1]);
624 # undef SWAP
625         }
626       else if (i == 1)
627         {
628           GLfloat f;
629 # define SWAP(A,B) f = A, A = B, B = -f
630           SWAP(t[0][0], t[0][1]);
631           SWAP(t[1][0], t[1][1]);
632           SWAP(t[2][0], t[2][1]);
633           SWAP(t[3][0], t[3][1]);
634 # undef SWAP
635         }
636
637       set_colors_alpha (exterior_color, 1-ratio);
638       glBindTexture (GL_TEXTURE_2D, lc->texids[tex[i + (top_p ? 3 : 0)]]);
639
640       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
641       do_normal (p[0][0], p[0][1], p[0][2],
642                  p[1][0], p[1][1], p[1][2],
643                  p[2][0], p[2][1], p[2][2]);
644       glTexCoord2fv(t[0]); glVertex3fv(p[0]);
645       glTexCoord2fv(t[1]); glVertex3fv(p[1]);
646       glTexCoord2fv(t[2]); glVertex3fv(p[2]);
647       glTexCoord2fv(t[3]); glVertex3fv(p[3]);
648       glEnd();
649       mi->polygon_count++;
650
651       /* The triangles between the quads */
652 # if 0
653       /* #### There is a fucking gap between the two black triangles 
654          that I can't figure out!  So instead of drawing the triangles,
655          we build a black shield around the middle bit in leviathan()
656          and count on back-face culling to have roughly the same effect.
657        */
658       if (!wire)
659         {
660           GLfloat pp[4][3];
661           memcpy (pp, p, sizeof(pp));
662           memcpy (pp[2], pp[1], sizeof(pp[1]));
663           pp[2][0] -= 0.5 * (1-ratio);
664           pp[2][1] -= 0.5 * (1-ratio);
665
666           glBindTexture (GL_TEXTURE_2D, 0);
667           set_colors_alpha (black_color, 1-ratio);
668
669           glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLES);
670           do_normal (pp[0][0], pp[0][1], pp[0][2],
671                      pp[2][0], pp[2][1], pp[2][2],
672                      pp[1][0], pp[1][1], pp[1][2]);
673           glVertex3fv(pp[0]);
674           glVertex3fv(pp[2]);
675           glVertex3fv(pp[1]);
676           glEnd();
677           mi->polygon_count++;
678         }
679 # endif
680
681       glPopMatrix();
682     }
683
684   if (! wire) glDisable (GL_BLEND);
685
686   glPopMatrix();
687 }
688
689
690 static int
691 lament_sphere (ModeInfo *mi, GLfloat ratio)
692 {
693   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
694   Bool wire = MI_IS_WIREFRAME(mi);
695   GLfloat size = 3; /* 3" square */
696   int polys = 0;
697   int facets = 16;  /* NxN grid on each face */
698   int face;
699   static const GLfloat norms[6][3] = {{ 0, -1, 0 }, {  0, 0, 1 }, { 0, 1, 0 },
700                                       { 0, 0, -1 }, { -1, 0, 0 }, { 1, 0, 0 }};
701   GLfloat s = 1.0 / facets;
702
703   /* The ratio used for the normals: linger on the square normals. */
704   GLfloat ratio2 = 1 - sin ((1 - ratio) / 2 * M_PI);
705   GLfloat r1 = 1 - ratio2 / 2;
706   GLfloat r2 =     ratio2 / 2;
707
708   glPushMatrix();
709   glTranslatef (-0.5, -0.5, -0.5);
710   glScalef (1/size, 1/size, 1/size);
711
712   set_colors (exterior_color);
713
714   for (face = 0; face < 6; face++)
715     {
716       GLfloat x0, y0;
717       for (y0 = 0; y0 < 1; y0 += s)
718         for (x0 = 0; x0 < 1; x0 += s)
719         {
720           int i;
721           GLfloat x1 = x0 + s;
722           GLfloat y1 = y0 + s;
723           GLfloat pa[4][3];     /* verts of the cube */
724           GLfloat pb[4][3];     /* verts of the transition to the sphere */
725           Bool frontp;
726           GLfloat norm[4][3];   /* normals of the transitional verts */
727
728           if (norms[face][0])
729             frontp = norms[face][0] < 0,
730             pa[0][1] = x0, pa[0][2] = y0, pa[0][0] = (frontp ? 0 : 1),
731             pa[1][1] = x1, pa[1][2] = y0, pa[1][0] = pa[0][0],
732             pa[2][1] = x1, pa[2][2] = y1, pa[2][0] = pa[0][0],
733             pa[3][1] = x0, pa[3][2] = y1, pa[3][0] = pa[0][0];
734           else if (norms[face][1])
735             frontp = norms[face][1] > 0,
736             pa[0][0] = x0, pa[0][2] = y0, pa[0][1] = (frontp ? 1 : 0),
737             pa[1][0] = x1, pa[1][2] = y0, pa[1][1] = pa[0][1],
738             pa[2][0] = x1, pa[2][2] = y1, pa[2][1] = pa[0][1],
739             pa[3][0] = x0, pa[3][2] = y1, pa[3][1] = pa[0][1];
740           else /* (norms[face][2]) */
741             frontp = norms[face][2] < 0,
742             pa[0][0] = x0, pa[0][1] = y0, pa[0][2] = (frontp ? 0 : 1),
743             pa[1][0] = x1, pa[1][1] = y0, pa[1][2] = pa[0][2],
744             pa[2][0] = x1, pa[2][1] = y1, pa[2][2] = pa[0][2],
745             pa[3][0] = x0, pa[3][1] = y1, pa[3][2] = pa[0][2];
746
747           for (i = 0; i < countof(pa); i++)
748             pa[i][0] *= size, pa[i][1] *= size, pa[i][2] *= size;
749
750           /* Convert square to sphere by treating as a normalized vector */
751           for (i = 0; i < countof(pa); i++)
752             {
753               GLfloat x = (pa[i][0] / size) - 0.5;
754               GLfloat y = (pa[i][1] / size) - 0.5;
755               GLfloat z = (pa[i][2] / size) - 0.5;
756               GLfloat d = sqrt (x*x + y*y + z*z) / 2;
757               x = x/d + size/2;
758               y = y/d + size/2;
759               z = z/d + size/2;
760
761               pb[i][0] = pa[i][0] + ((x - pa[i][0]) * ratio);
762               pb[i][1] = pa[i][1] + ((y - pa[i][1]) * ratio);
763               pb[i][2] = pa[i][2] + ((z - pa[i][2]) * ratio);
764             }
765
766           /* The normals of an intermediate point are the weighted average
767              of the cube's orthogonal normals, and the sphere's radial
768              normals: early in the sequence, the edges are sharp, but they
769              soften as it expands. */
770           {
771             XYZ na, pa0, pa1, pa2;
772             pa0.x = pa[0][0]; pa0.y = pa[0][1]; pa0.z = pa[0][2];
773             pa1.x = pa[1][0]; pa1.y = pa[1][1]; pa1.z = pa[1][2];
774             pa2.x = pa[2][0]; pa2.y = pa[2][1]; pa2.z = pa[2][2];
775             na = calc_normal (pa0, pa1, pa2);
776
777             for (i = 0; i < countof(pb); i++)
778               {
779                 GLfloat d;
780                 XYZ nb;
781
782                 nb.x = pb[i][0];
783                 nb.y = pb[i][1];
784                 nb.z = pb[i][2];
785                 d = sqrt (nb.x*nb.x + nb.y*nb.y + nb.z*nb.z);  /* normalize */
786                 nb.x /= d;
787                 nb.y /= d;
788                 nb.z /= d;
789
790                 norm[i][0] = (na.x * r1) + (nb.x * r2);  /* weighted */
791                 norm[i][1] = (na.y * r1) + (nb.y * r2);
792                 norm[i][2] = (na.z * r1) + (nb.z * r2);
793               }
794           }
795
796           if (! wire)
797             glBindTexture (GL_TEXTURE_2D, lc->texids[face]);
798
799           glFrontFace (frontp ? GL_CW : GL_CCW);
800           glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
801
802           texturize_vert (mi, face+1, pa[0]);
803           glNormal3fv (norm[0]);
804           glVertex3fv (pb[0]);
805
806           texturize_vert (mi, face+1, pa[1]);
807           glNormal3fv (norm[1]);
808           glVertex3fv (pb[1]);
809
810           texturize_vert (mi, face+1, pa[2]);
811           glNormal3fv (norm[2]);
812           glVertex3fv (pb[2]);
813
814           texturize_vert (mi, face+1, pa[3]);
815           glNormal3fv (norm[3]);
816           glVertex3fv (pb[3]);
817
818           glEnd();
819           polys++;
820         }
821     }
822
823   glPopMatrix();
824
825   return polys;
826 }
827
828
829 static void
830 draw (ModeInfo *mi)
831 {
832   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
833   Bool wire = MI_IS_WIREFRAME(mi);
834
835   mi->polygon_count = 0;
836
837   if (!wire)
838     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
839   else
840     glClear(GL_COLOR_BUFFER_BIT);
841
842   glPushMatrix();
843
844   gltrackball_rotate (lc->trackball);
845
846   /* Make into the screen be +Y, right be +X, and up be +Z. */
847   glRotatef (-90.0, 1.0, 0.0, 0.0);
848
849   scale_for_window (mi);
850
851   /* Apply rotation to the object. */
852   if (lc->type != LAMENT_LID_ZOOM)
853     get_rotation (lc->rot, &lc->rotx, &lc->roty, &lc->rotz,
854                   !lc->button_down_p);
855 # ifdef DEBUG_MODE
856   lc->rotx = 0.18;
857   lc->roty = 0.22;
858   lc->rotx = lc->roty = 0;
859   lc->rotz = 0;
860 # endif
861
862   glRotatef (lc->rotx * 360, 1, 0, 0);
863   glRotatef (lc->roty * 360, 0, 1, 0);
864   glRotatef (lc->rotz * 360, 0, 0, 1);
865
866   glScalef (0.5, 0.5, 0.5);
867
868   switch (lc->type)
869     {
870     case LAMENT_BOX:
871       glCallList (lc->dlists[OBJ_BOX]);
872       mi->polygon_count += lc->polys[OBJ_BOX];
873       break;
874
875     case LAMENT_STAR_OUT:
876     case LAMENT_STAR_ROT:
877     case LAMENT_STAR_ROT_IN:
878     case LAMENT_STAR_ROT_OUT:
879     case LAMENT_STAR_UNROT:
880     case LAMENT_STAR_IN:
881       glTranslatef (0.0, 0.0, lc->anim_z/2);
882       glRotatef (lc->anim_r/2, 0.0, 0.0, 1.0);
883       glCallList (lc->dlists[OBJ_STAR_U]);
884       mi->polygon_count += lc->polys[OBJ_STAR_U];
885
886       glTranslatef (0.0, 0.0, -lc->anim_z);
887       glRotatef (-lc->anim_r, 0.0, 0.0, 1.0);
888       glCallList (lc->dlists[OBJ_STAR_D]);
889       mi->polygon_count += lc->polys[OBJ_STAR_D];
890       break;
891
892     case LAMENT_TETRA_UNE:
893     case LAMENT_TETRA_USW:
894     case LAMENT_TETRA_DWN:
895     case LAMENT_TETRA_DSE:
896       {
897         int magic;
898         GLfloat x, y, z;
899         switch (lc->type) {
900         case LAMENT_TETRA_UNE: magic = OBJ_TETRA_UNE; x= 1; y= 1; z= 1; break;
901         case LAMENT_TETRA_USW: magic = OBJ_TETRA_USW; x= 1; y= 1; z=-1; break;
902         case LAMENT_TETRA_DWN: magic = OBJ_TETRA_DWN; x= 1; y=-1; z= 1; break;
903         case LAMENT_TETRA_DSE: magic = OBJ_TETRA_DSE; x=-1; y= 1; z= 1; break;
904         default: abort(); break;
905         }
906         glCallList(lc->dlists[OBJ_TETRA_BASE]);
907         mi->polygon_count += lc->polys[OBJ_TETRA_BASE];
908         if (magic != OBJ_TETRA_UNE) glCallList (lc->dlists[OBJ_TETRA_UNE]);
909         if (magic != OBJ_TETRA_USW) glCallList (lc->dlists[OBJ_TETRA_USW]);
910         if (magic != OBJ_TETRA_DWN) glCallList (lc->dlists[OBJ_TETRA_DWN]);
911         if (magic != OBJ_TETRA_DSE) glCallList (lc->dlists[OBJ_TETRA_DSE]);
912         glRotatef (lc->anim_r, x, y, z);
913         glCallList (lc->dlists[magic]);
914         mi->polygon_count += lc->polys[magic] * 3;
915       }
916       break;
917
918     case LAMENT_LID_OPEN:
919     case LAMENT_LID_CLOSE:
920     case LAMENT_LID_ZOOM:
921       {
922         GLfloat d = 0.21582;
923         int i;
924         const int lists[4] = { OBJ_LID_A, OBJ_LID_B, OBJ_LID_C, OBJ_LID_D };
925
926         lc->facing_p = facing_screen_p (mi);
927
928         if (lc->anim_z < 0.5)
929           glTranslatef (0, -30 * lc->anim_z, 0);    /* zoom */
930         else
931           glTranslatef (8 * (0.5 - (lc->anim_z - 0.5)), 0, 0);
932
933         glCallList (lc->dlists[OBJ_LID_BASE]);
934         mi->polygon_count += lc->polys[OBJ_LID_BASE];
935         for (i = 0; i < countof(lists); i++)
936           {
937             glPushMatrix();
938             glRotatef (90 * i, 0, 1, 0);
939             glTranslatef (-d, -0.5, d);
940             glRotatef (-45, 0, 1, 0);
941             glRotatef (-lc->anim_r, 1, 0, 0);
942             glRotatef (45, 0, 1, 0);
943             glTranslatef (d, 0.5, -d);
944             glRotatef (-90 * i, 0, 1, 0);
945             glCallList (lc->dlists[lists[i]]);
946             mi->polygon_count += lc->polys[lists[i]];
947             glPopMatrix();
948           }
949
950 # ifdef DEBUG_MODE
951         if (lc->facing_p)
952           {
953             glColor3f(1, 0, 0);
954             glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
955             glVertex3f (-0.49, 0.49, -0.49);
956             glVertex3f ( 0.49, 0.49, -0.49);
957             glVertex3f ( 0.49, 0.49,  0.49);
958             glVertex3f (-0.49, 0.49,  0.49);
959             glEnd();
960             mi->polygon_count++;
961           }
962 # endif /* DEBUG_MODE */
963       }
964       break;
965
966     case LAMENT_TASER_OUT:
967     case LAMENT_TASER_SLIDE:
968     case LAMENT_TASER_SLIDE_IN:
969     case LAMENT_TASER_IN:
970
971       glTranslatef (0, -lc->anim_z/2, 0);
972       glCallList (lc->dlists[OBJ_TASER_BASE]);
973       mi->polygon_count += lc->polys[OBJ_TASER_BASE];
974
975       glTranslatef (0, lc->anim_z, 0);
976       glCallList (lc->dlists[OBJ_TASER_A]);
977       mi->polygon_count += lc->polys[OBJ_TASER_A];
978
979       glTranslatef (lc->anim_y, 0, 0);
980       glCallList (lc->dlists[OBJ_TASER_B]);
981       mi->polygon_count += lc->polys[OBJ_TASER_B];
982       break;
983
984     case LAMENT_PILLAR_OUT:
985     case LAMENT_PILLAR_SPIN:
986     case LAMENT_PILLAR_IN:
987
988       glCallList (lc->dlists[OBJ_PILLAR_BASE]);
989       mi->polygon_count += lc->polys[OBJ_PILLAR_BASE];
990
991       glPushMatrix();
992       if (lc->anim_z == 1 || lc->anim_z == 3)
993         {
994           glRotatef (lc->anim_r, 0, 0, 1);
995           glTranslatef (0, 0, lc->anim_y);
996         }
997       glCallList (lc->dlists[OBJ_PILLAR_A]);
998       mi->polygon_count += lc->polys[OBJ_PILLAR_A];
999       glPopMatrix();
1000
1001       glPushMatrix();
1002       if (lc->anim_z == 2 || lc->anim_z == 3)
1003         {
1004           glRotatef (lc->anim_r, 0, 0, 1);
1005           glTranslatef (0, 0, -lc->anim_y);
1006         }
1007       glCallList (lc->dlists[OBJ_PILLAR_B]);
1008       mi->polygon_count += lc->polys[OBJ_PILLAR_B];
1009       glPopMatrix();
1010       break;
1011
1012     case LAMENT_SPHERE_OUT:
1013     case LAMENT_SPHERE_IN:
1014       mi->polygon_count += lament_sphere (mi, lc->anim_y);
1015       break;
1016
1017     case LAMENT_LEVIATHAN_SPIN:
1018     case LAMENT_LEVIATHAN_UNSPIN:
1019     case LAMENT_LEVIATHAN_FADE:
1020     case LAMENT_LEVIATHAN_UNFADE:
1021     case LAMENT_LEVIATHAN_TWIST:
1022     case LAMENT_LEVIATHAN_UNTWIST:
1023       {
1024         /* These normals are hard to compute, so I pulled them from the
1025            model. */
1026         const GLfloat axes[6][4] =
1027           {{ OBJ_ISO_UNE,  0.633994, 0.442836,   0.633994 },
1028            { OBJ_ISO_USW,  0.442836,  0.633994, -0.633994 },
1029            { OBJ_ISO_DSE, -0.633994,  0.633994,  0.442836 },
1030            { OBJ_ISO_SWD, -0.633994, -0.442836, -0.633994 },
1031            { OBJ_ISO_DEN, -0.442836, -0.633994,  0.633994 },
1032            { OBJ_ISO_UNW,  0.633994, -0.633994, -0.442836 }};
1033         int i;
1034
1035         GLfloat s = (1 - lc->anim_z);
1036         GLfloat s2 = MAX (0, 360 - lc->anim_r) / 360.0;
1037         Bool blendp = 0;
1038
1039         switch (lc->type) {
1040         case LAMENT_LEVIATHAN_SPIN: break;
1041         case LAMENT_LEVIATHAN_UNSPIN: s2 = 1 - s2; break;
1042         default: s2 = 0; blendp = 1; break;
1043         }
1044
1045         if (wire) blendp = 0;
1046
1047         s  = (s * 0.6) + 0.4;
1048
1049         leviathan (mi, 1 - s2, 1, True);
1050         glCallList (lc->dlists[OBJ_ISO_BASE_A]);
1051         mi->polygon_count += lc->polys[OBJ_ISO_BASE_A];
1052
1053         glPushMatrix();
1054         glScalef (s2, s2, s2);
1055         glCallList (lc->dlists[OBJ_ISO_USE]);
1056         mi->polygon_count += lc->polys[OBJ_ISO_USE];
1057         glPopMatrix();
1058
1059         glPushMatrix();
1060         glRotatef (lc->anim_y, 1, -1, 1);
1061         glCallList (lc->dlists[OBJ_ISO_BASE_B]);
1062         mi->polygon_count += lc->polys[OBJ_ISO_BASE_B];
1063         leviathan (mi, 1 - s2, 1, False);
1064         glPopMatrix();
1065
1066         if (blendp)
1067           {
1068 # ifndef HAVE_JWZGLES /* no glBlendColor */
1069             glEnable (GL_BLEND);
1070             glBlendFunc (GL_CONSTANT_ALPHA, GL_SRC_ALPHA);
1071             glBlendColor (1, 1, 1, MAX(0, 1-(lc->anim_z * 3)));
1072 # endif
1073           }
1074
1075         for (i = 0; i < countof(axes); i++)
1076           {
1077             glPushMatrix();
1078             glRotatef (lc->anim_r, axes[i][1], axes[i][2], axes[i][3]);
1079             glScalef (s, s, s);
1080             glCallList (lc->dlists[(int) axes[i][0]]);
1081             mi->polygon_count += lc->polys[(int) axes[i][0]];
1082             glPopMatrix();
1083             if (i == 2)
1084               glRotatef (lc->anim_y, 1, -1, 1);
1085           }
1086
1087         if (blendp) glDisable (GL_BLEND);
1088
1089         glPushMatrix();
1090         glScalef (s2, s2, s2);
1091         glCallList (lc->dlists[OBJ_ISO_DWN]);
1092         mi->polygon_count += lc->polys[OBJ_ISO_DWN];
1093         glPopMatrix();
1094       }
1095       break;
1096
1097     case LAMENT_LEVIATHAN_COLLAPSE:
1098     case LAMENT_LEVIATHAN_EXPAND:
1099       {
1100         glPushMatrix();
1101         leviathan (mi, 1, lc->anim_y, True);
1102         glRotatef (180, 1, -1, 1);
1103         leviathan (mi, 1, lc->anim_y, False);
1104         glPopMatrix();
1105         folding_walls (mi, lc->anim_y, True);
1106         folding_walls (mi, lc->anim_y, False);
1107       }
1108       break;
1109
1110     default:
1111       abort();
1112       break;
1113     }
1114
1115   glPopMatrix();
1116 }
1117
1118
1119 /* Rather than just picking states randomly, pick an ordering randomly, do it,
1120    and then re-randomize.  That way one can be assured of seeing all states in
1121    a short time period, though not always in the same order (it's frustrating
1122    to see it pick the same state 5x in a row.)  Though, that can still happen,
1123    since states are in the list multiple times as a way of giving them
1124    probabilities.
1125  */
1126 static void
1127 shuffle_states (lament_configuration *lc)
1128 {
1129   int i;
1130   for (i = 0; i < lc->nstates; i++)
1131     {
1132       int a = random() % lc->nstates;
1133       lament_type swap = lc->states[a];
1134       lc->states[a] = lc->states[i];
1135       lc->states[i] = swap;
1136     }
1137 }
1138
1139
1140 static void
1141 animate (ModeInfo *mi)
1142 {
1143   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1144   int pause = 10;
1145   int pause2 = 120;
1146   GLfloat speed = (lc->ffwdp ? 20 : 1);
1147
1148   switch (lc->type)
1149     {
1150     case LAMENT_BOX:
1151       {
1152         lc->state++;
1153         if (lc->state >= lc->nstates)
1154           {
1155             shuffle_states (lc);
1156             lc->state = 0;
1157           }
1158         lc->type = lc->states[lc->state];
1159
1160         if (lc->type == LAMENT_BOX)
1161           lc->anim_pause = pause2;
1162
1163         lc->anim_r = 0.0;
1164         lc->anim_y = 0.0;
1165         lc->anim_z = 0.0;
1166       }
1167       break;
1168
1169       /* -------------------------------------------------------------- */
1170
1171     case LAMENT_STAR_OUT:
1172       lc->anim_z += 0.01 * speed;
1173       if (lc->anim_z >= 1.0)
1174         {
1175           lc->anim_z = 1.0;
1176           lc->type = LAMENT_STAR_ROT;
1177           lc->anim_pause = pause;
1178         }
1179       break;
1180
1181     case LAMENT_STAR_ROT:
1182       lc->anim_r += 1.0 * speed;
1183       if (lc->anim_r >= 45.0)
1184         {
1185           lc->anim_r = 45.0;
1186           lc->type = LAMENT_STAR_ROT_IN;
1187           lc->anim_pause = pause;
1188         }
1189       break;
1190
1191     case LAMENT_STAR_ROT_IN:
1192       lc->anim_z -= 0.01 * speed;
1193       if (lc->anim_z <= 0.0)
1194         {
1195           lc->anim_z = 0.0;
1196           lc->type = LAMENT_STAR_ROT_OUT;
1197           lc->anim_pause = pause2 * (1 + frand(2) + frand(2));
1198         }
1199       break;
1200
1201     case LAMENT_STAR_ROT_OUT:
1202       lc->anim_z += 0.01 * speed;
1203       if (lc->anim_z >= 1.0)
1204         {
1205           lc->anim_z = 1.0;
1206           lc->type = LAMENT_STAR_UNROT;
1207           lc->anim_pause = pause;
1208         }
1209       break;
1210       
1211     case LAMENT_STAR_UNROT:
1212       lc->anim_r -= 1.0 * speed;
1213       if (lc->anim_r <= 0.0)
1214         {
1215           lc->anim_r = 0.0;
1216           lc->type = LAMENT_STAR_IN;
1217           lc->anim_pause = pause;
1218         }
1219       break;
1220
1221     case LAMENT_STAR_IN:
1222       lc->anim_z -= 0.01 * speed;
1223       if (lc->anim_z <= 0.0)
1224         {
1225           lc->anim_z = 0.0;
1226           lc->type = LAMENT_BOX;
1227           lc->anim_pause = pause2;
1228         }
1229       break;
1230
1231       /* -------------------------------------------------------------- */
1232
1233     case LAMENT_TETRA_UNE:
1234     case LAMENT_TETRA_USW:
1235     case LAMENT_TETRA_DWN:
1236     case LAMENT_TETRA_DSE:
1237
1238       lc->anim_r += 1.0 * speed;
1239       if (lc->anim_r >= 360.0)
1240         {
1241           lc->anim_r = 0.0;
1242           lc->type = LAMENT_BOX;
1243           lc->anim_pause = pause2;
1244         }
1245       else if (lc->anim_r > 119.0 && lc->anim_r <= 120.0)
1246         {
1247           lc->anim_r = 120.0;
1248           lc->anim_pause = pause;
1249         }
1250       else if (lc->anim_r > 239.0 && lc->anim_r <= 240.0)
1251         {
1252           lc->anim_r = 240.0;
1253           lc->anim_pause = pause;
1254         }
1255       break;
1256
1257       /* -------------------------------------------------------------- */
1258
1259     case LAMENT_LID_OPEN:
1260       lc->anim_r += 1.0 * speed;
1261
1262       if (lc->anim_r >= 112.0)
1263         {
1264           lc->anim_r = 112.0;
1265           lc->anim_z = 0.0;
1266           lc->anim_pause = pause2;
1267           lc->type = (lc->facing_p ? LAMENT_LID_ZOOM : LAMENT_LID_CLOSE);
1268         }
1269       break;
1270
1271     case LAMENT_LID_CLOSE:
1272       lc->anim_r -= 1.0 * speed;
1273       if (lc->anim_r <= 0.0)
1274         {
1275           lc->anim_r = 0.0;
1276           lc->type = LAMENT_BOX;
1277           lc->anim_pause = pause2;
1278         }
1279       break;
1280
1281     case LAMENT_LID_ZOOM:
1282       lc->anim_z += 0.01 * speed;
1283       if (lc->anim_z > 1.0)
1284         {
1285           lc->anim_r = 0.0;
1286           lc->anim_z = 0.0;
1287           lc->type = LAMENT_BOX;
1288         }
1289       break;
1290
1291       /* -------------------------------------------------------------- */
1292
1293     case LAMENT_TASER_OUT:
1294       lc->anim_z += 0.005 * speed;
1295       if (lc->anim_z >= 0.5)
1296         {
1297           lc->anim_z = 0.5;
1298           lc->type = LAMENT_TASER_SLIDE;
1299           lc->anim_pause = pause * (1 + frand(5) + frand(5));
1300         }
1301       break;
1302
1303     case LAMENT_TASER_SLIDE:
1304       lc->anim_y += 0.005 * speed;
1305       if (lc->anim_y >= 0.255)
1306         {
1307           lc->anim_y = 0.255;
1308           lc->type = LAMENT_TASER_SLIDE_IN;
1309           lc->anim_pause = pause2 * (1 + frand(5) + frand(5));
1310         }
1311       break;
1312
1313     case LAMENT_TASER_SLIDE_IN:
1314       lc->anim_y -= 0.0025 * speed;
1315       if (lc->anim_y <= 0.0)
1316         {
1317           lc->anim_y = 0.0;
1318           lc->type = LAMENT_TASER_IN;
1319           lc->anim_pause = pause;
1320         }
1321       break;
1322
1323     case LAMENT_TASER_IN:
1324       lc->anim_z -= 0.0025 * speed;
1325       if (lc->anim_z <= 0.0)
1326         {
1327           lc->anim_z = 0.0;
1328           lc->type = LAMENT_BOX;
1329           lc->anim_pause = pause2;
1330         }
1331       break;
1332
1333       /* -------------------------------------------------------------- */
1334
1335     case LAMENT_PILLAR_OUT:
1336
1337       if (lc->anim_y == 0)  /* mostly in */
1338         lc->anim_y += 0.005 * ((random() % 5) ? -1 : 1) * speed;
1339       else if (lc->anim_y > 0)
1340         lc->anim_y += 0.005 * speed;
1341       else
1342         lc->anim_y -= 0.001 * speed;
1343
1344       if (lc->anim_z == 0)
1345         {
1346           int i = (random() % 7);   /* A, B or both */
1347           if      (i == 0) lc->anim_z = 3;
1348           else if (i < 5)  lc->anim_z = 2;
1349           else             lc->anim_z = 1;
1350
1351           /* We can do quarter turns, because it's radially symmetrical. */
1352           lc->anim_r =  90.0 * (1 + frand(6)) * RANDSIGN();
1353         }
1354       if (lc->anim_y > 0.4)
1355         {
1356           lc->anim_y = 0.4;
1357           lc->type = LAMENT_PILLAR_SPIN;
1358           lc->anim_pause = pause;
1359         }
1360       else if (lc->anim_y < -0.03)
1361         {
1362           lc->anim_y = -0.03;
1363           lc->type = LAMENT_PILLAR_SPIN;
1364           lc->anim_pause = pause;
1365         }
1366       break;
1367
1368     case LAMENT_PILLAR_SPIN:
1369       {
1370         Bool negp = (lc->anim_r < 0);
1371         lc->anim_r += (negp ? 1 : -1) * speed;
1372         if (negp ? lc->anim_r > 0 : lc->anim_r < 0)
1373           {
1374             lc->anim_r = 0;
1375             lc->type = LAMENT_PILLAR_IN;
1376           }
1377       }
1378       break;
1379
1380     case LAMENT_PILLAR_IN:
1381       {
1382         Bool negp = (lc->anim_y < 0);
1383         lc->anim_y += (negp ? 1 : -1) * 0.005 * speed;
1384         if (negp ? lc->anim_y > 0 : lc->anim_y < 0)
1385           {
1386             lc->anim_y = 0;
1387             lc->anim_z = 0;
1388             lc->type = LAMENT_BOX;
1389             lc->anim_pause = pause;
1390           }
1391       }
1392       break;
1393
1394       /* -------------------------------------------------------------- */
1395
1396     case LAMENT_SPHERE_OUT:
1397       {
1398         lc->anim_y += 0.01 * speed;
1399         if (lc->anim_y >= 1)
1400           {
1401             lc->anim_y = 1;
1402             lc->type = LAMENT_SPHERE_IN;
1403             lc->anim_pause = pause2 * (1 + frand(1) + frand(1));
1404           }
1405       }
1406       break;
1407
1408     case LAMENT_SPHERE_IN:
1409       {
1410         lc->anim_y -= 0.01 * speed;
1411         if (lc->anim_y <= 0)
1412           {
1413             lc->anim_y = 0;
1414             lc->type = LAMENT_BOX;
1415             lc->anim_pause = pause;
1416           }
1417       }
1418       break;
1419
1420       /* -------------------------------------------------------------- */
1421
1422     case LAMENT_LEVIATHAN_SPIN:
1423       lc->anim_r += 3.5 * speed;
1424       if (lc->anim_r >= 360 * 3)
1425         {
1426           lc->anim_r = 0;
1427           lc->type = LAMENT_LEVIATHAN_FADE;
1428           lc->anim_pause = 0;
1429         }
1430       break;
1431
1432     case LAMENT_LEVIATHAN_FADE:
1433       lc->anim_z += 0.01 * speed;
1434       if (lc->anim_z >= 1)
1435         {
1436           lc->anim_z = 1;
1437           lc->type = LAMENT_LEVIATHAN_TWIST;
1438           lc->anim_pause = 0;
1439         }
1440       break;
1441
1442     case LAMENT_LEVIATHAN_TWIST:
1443       lc->anim_y += 2 * speed;
1444       lc->anim_z = 1;
1445       if (lc->anim_y >= 180)
1446         {
1447           lc->anim_y = 0;
1448           lc->type = LAMENT_LEVIATHAN_COLLAPSE;
1449           lc->anim_pause = 0;
1450         }
1451       break;
1452
1453     case LAMENT_LEVIATHAN_COLLAPSE:
1454       lc->anim_y += 0.01 * speed;
1455       if (lc->anim_y >= 1)
1456         {
1457           lc->anim_y = 1.0;
1458           lc->type = LAMENT_LEVIATHAN_EXPAND;
1459           lc->anim_pause = pause2 * 4;
1460         }
1461       break;
1462
1463     case LAMENT_LEVIATHAN_EXPAND:
1464       lc->anim_y -= 0.005 * speed;
1465       if (lc->anim_y <= 0)
1466         {
1467           lc->anim_y = 180;
1468           lc->type = LAMENT_LEVIATHAN_UNTWIST;
1469         }
1470       break;
1471
1472     case LAMENT_LEVIATHAN_UNTWIST:
1473       lc->anim_y -= 2 * speed;
1474       lc->anim_z = 1;
1475       if (lc->anim_y <= 0)
1476         {
1477           lc->anim_y = 0;
1478           lc->type = LAMENT_LEVIATHAN_UNFADE;
1479           lc->anim_pause = 0;
1480         }
1481       break;
1482
1483     case LAMENT_LEVIATHAN_UNFADE:
1484       lc->anim_z -= 0.1 * speed;
1485       if (lc->anim_z <= 0)
1486         {
1487           lc->anim_z = 0;
1488           lc->type = LAMENT_LEVIATHAN_UNSPIN;
1489           lc->anim_pause = 0;
1490         }
1491       break;
1492
1493     case LAMENT_LEVIATHAN_UNSPIN:
1494       lc->anim_r += 3.5 * speed;
1495       if (lc->anim_r >= 360 * 2)
1496         {
1497           lc->anim_r = 0;
1498           lc->type = LAMENT_BOX;
1499           lc->anim_pause = pause2;
1500         }
1501       break;
1502
1503     default:
1504       abort();
1505       break;
1506     }
1507
1508 # ifdef DEBUG_MODE
1509
1510   lc->anim_pause = 0;
1511
1512   if (lc->type == LAMENT_BOX)
1513     lc->type = DEBUG_MODE;
1514
1515   if (lc->ffwdp)
1516     {
1517       lc->ffwdp = 0;
1518       while (lc->type != DEBUG_MODE)
1519         animate (mi);
1520     }
1521
1522 # else /* !DEBUG_MODE */
1523
1524   if (lc->ffwdp && lc->type == LAMENT_BOX)
1525     {
1526       lc->ffwdp = 0;
1527       while (lc->type == LAMENT_BOX)
1528         animate (mi);
1529       lc->anim_pause = 0;
1530     }
1531
1532 # endif /* !DEBUG_MODE */
1533 }
1534
1535
1536 static void
1537 gl_init (ModeInfo *mi)
1538 {
1539   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1540   Bool wire = MI_IS_WIREFRAME(mi);
1541   int i;
1542
1543   if (wire)
1544     do_texture = False;
1545
1546   if (!wire)
1547     {
1548       static const GLfloat pos0[]  = { -4.0,  2.0, 5.0, 1.0 };
1549       static const GLfloat pos1[]  = {  6.0, -1.0, 3.0, 1.0 };
1550
1551       static const GLfloat amb0[]  = { 0.7, 0.7, 0.7, 1.0 };
1552 /*    static const GLfloat amb1[]  = { 0.7, 0.0, 0.0, 1.0 }; */
1553       static const GLfloat dif0[]  = { 1.0, 1.0, 1.0, 1.0 };
1554       static const GLfloat dif1[]  = { 0.3, 0.1, 0.1, 1.0 };
1555
1556       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1557       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1558
1559       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb0);
1560 /*    glLightfv(GL_LIGHT1, GL_AMBIENT,  amb1); */
1561       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif0);
1562       glLightfv(GL_LIGHT1, GL_DIFFUSE,  dif1);
1563
1564       glEnable(GL_LIGHTING);
1565       glEnable(GL_LIGHT0);
1566 /*    glEnable(GL_LIGHT1); */
1567
1568       glEnable(GL_DEPTH_TEST);
1569       glEnable(GL_TEXTURE_2D);
1570       glEnable(GL_NORMALIZE);
1571       glEnable(GL_CULL_FACE);
1572     }
1573
1574   if (do_texture)
1575     {
1576       int i;
1577       for (i = 0; i < countof(lc->texids); i++)
1578         glGenTextures(1, &lc->texids[i]);
1579
1580       lc->texture = image_data_to_ximage (mi->dpy, mi->xgwa.visual,
1581                                           lament512_png, sizeof(lament512_png));
1582
1583       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
1584       /* messes up -fps */
1585       /* glPixelStorei(GL_UNPACK_ROW_LENGTH, lc->texture->width); */
1586
1587       for (i = 0; i < countof(lc->texids); i++)
1588         {
1589           int height = lc->texture->width;      /* assume square */
1590           glBindTexture(GL_TEXTURE_2D, lc->texids[i]);
1591
1592           clear_gl_error();
1593           glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1594                        lc->texture->width, height, 0,
1595                        GL_RGBA, GL_UNSIGNED_BYTE,
1596                        (lc->texture->data +
1597                         (lc->texture->bytes_per_line * height * i)));
1598           check_gl_error("texture");
1599
1600           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1601           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1602           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1603           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1604           check_gl_error("texture");
1605
1606           /* This makes scaled pixmaps tolerable to look at. */
1607 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
1608 #   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
1609 # endif
1610 # ifdef GL_TEXTURE_LOD_BIAS
1611           glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
1612 # endif
1613           clear_gl_error();  /* invalid enum on iPad 3 */
1614         }
1615     }
1616
1617   for (i = 0; i < countof(all_objs); i++)
1618     {
1619       GLfloat s = 1/3.0;  /* box is 3" square */
1620       const struct gllist *L = *all_objs[i];
1621       const GLfloat *f = (const GLfloat *) L->data;
1622       int j;
1623
1624       lc->dlists[i] = glGenLists(1);
1625       lc->polys[i] = L->points / 3;
1626       glNewList(lc->dlists[i], GL_COMPILE);
1627       if (L->primitive != GL_TRIANGLES) abort();
1628       if (L->format != GL_N3F_V3F) abort();
1629
1630       glPushMatrix();
1631       glTranslatef (-0.5, -0.5, -0.5);
1632       glScalef (s, s, s);
1633       
1634       for (j = 0; j < L->points; j += 3)
1635         {
1636           int face, outerp;
1637           Bool blackp = (i == OBJ_ISO_BASE_A || i == OBJ_ISO_BASE_B);
1638           which_face (mi, f, &face, &outerp); /* from norm of first vert */
1639
1640           set_colors (outerp ? exterior_color :
1641                       blackp ? black_color : interior_color);
1642           glBindTexture (GL_TEXTURE_2D, 
1643                          (outerp ? lc->texids[face-1] :
1644                           blackp ? 0 : lc->texids[6]));
1645
1646           glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
1647           if (face) texturize_vert (mi, face, f+3);
1648           glNormal3fv (f); f += 3; glVertex3fv (f); f += 3;
1649           if (face) texturize_vert (mi, face, f+3);
1650           glNormal3fv (f); f += 3; glVertex3fv (f); f += 3;
1651           if (face) texturize_vert (mi, face, f+3);
1652           glNormal3fv (f); f += 3; glVertex3fv (f); f += 3;
1653           glEnd();
1654         }
1655       glPopMatrix();
1656
1657       glEndList();
1658     }
1659 }
1660
1661
1662 ENTRYPOINT Bool
1663 lament_handle_event (ModeInfo *mi, XEvent *event)
1664 {
1665   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1666
1667   if (gltrackball_event_handler (event, lc->trackball,
1668                                  MI_WIDTH (mi), MI_HEIGHT (mi),
1669                                  &lc->button_down_p))
1670     return True;
1671   else if (event->xany.type == KeyPress)
1672     {
1673       KeySym keysym;
1674       char c = 0;
1675       XLookupString (&event->xkey, &c, 1, &keysym, 0);
1676       if (c == ' ' || c == '\t')
1677         {
1678           lc->ffwdp = True;
1679           return True;
1680         }
1681     }
1682
1683   return False;
1684 }
1685
1686
1687 ENTRYPOINT void
1688 reshape_lament (ModeInfo *mi, int width, int height)
1689 {
1690   GLfloat h = (GLfloat) height / (GLfloat) width;
1691   glViewport(0, 0, (GLint) width, (GLint) height);
1692
1693   glMatrixMode(GL_PROJECTION);
1694   glLoadIdentity();
1695   glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
1696   glMatrixMode(GL_MODELVIEW);
1697   glLoadIdentity();
1698   glTranslatef(0.0, 0.0, -40.0);
1699   glClear(GL_COLOR_BUFFER_BIT);
1700 }
1701
1702
1703 ENTRYPOINT void
1704 init_lament (ModeInfo *mi)
1705 {
1706   lament_configuration *lc;
1707   int i;
1708   MI_INIT (mi, lcs);
1709
1710   lc = &lcs[MI_SCREEN(mi)];
1711
1712   {
1713     double rot_speed = 0.5;
1714     lc->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
1715     lc->trackball = gltrackball_init (True);
1716   }
1717
1718   lc->type = LAMENT_BOX;
1719   lc->anim_pause = 300 + (random() % 100);
1720
1721   if ((lc->glx_context = init_GL(mi)) != NULL)
1722     {
1723       reshape_lament(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1724       gl_init(mi);
1725     }
1726
1727   lc->states = (lament_type *) calloc (200, sizeof (*lc->states));
1728   lc->nstates = 0;
1729
1730 # define PUSH(N,WHICH) \
1731     for (i = 0; i < N; i++) lc->states[lc->nstates++] = WHICH
1732
1733   PUSH (4, LAMENT_TETRA_UNE);           /* most common */
1734   PUSH (4, LAMENT_TETRA_USW);
1735   PUSH (4, LAMENT_TETRA_DWN);
1736   PUSH (4, LAMENT_TETRA_DSE);
1737
1738   PUSH (8, LAMENT_STAR_OUT);            /* pretty common */
1739   PUSH (8, LAMENT_TASER_OUT);
1740   PUSH (8, LAMENT_PILLAR_OUT);
1741
1742   PUSH (4, LAMENT_LID_OPEN);            /* rare */
1743   PUSH (2, LAMENT_SPHERE_OUT);          /* rare */
1744   PUSH (1, LAMENT_LEVIATHAN_SPIN);      /* very rare */
1745
1746   PUSH (35, LAMENT_BOX);                /* rest state */
1747 # undef PUSH
1748
1749   shuffle_states (lc);
1750
1751 # ifdef DEBUG_MODE
1752   lc->type = DEBUG_MODE;
1753   lc->anim_pause = 0;
1754 # endif
1755
1756 }
1757
1758
1759 ENTRYPOINT void
1760 draw_lament (ModeInfo *mi)
1761 {
1762   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1763   Display *dpy = MI_DISPLAY(mi);
1764   Window window = MI_WINDOW(mi);
1765
1766   if (!lc->glx_context)
1767     return;
1768
1769   glDrawBuffer(GL_BACK);
1770
1771   glXMakeCurrent(dpy, window, *(lc->glx_context));
1772   draw(mi);
1773   if (mi->fps_p) do_fps (mi);
1774
1775   glFinish();
1776   glXSwapBuffers(dpy, window);
1777
1778   if (!lc->ffwdp && lc->anim_pause)
1779     lc->anim_pause--;
1780   else
1781     animate(mi);
1782 }
1783
1784 XSCREENSAVER_MODULE ("Lament", lament)
1785
1786 #endif /* USE_GL */