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