From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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 refresh_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   /* Constrain it to roughly life-sized on the screen, not huge.
329    */
330 # ifdef HAVE_MOBILE
331   if (size > 768)  /* iPad retina / iPhone 6 */
332     target_size *= 1.5;
333   else
334 # endif
335     {
336       GLfloat max = 500;  /* 3" on my screen... */
337       if (target_size > max)
338         target_size = max;
339     }
340
341   /* But if that would make the image larger than target_size, scale it
342      back down again.  The image-map bits we have are 512x512, so if the
343      image is magnified a lot, it looks pretty blocky.  It's better to
344      have a 512x512 animation on a 1920x1080 screen that looks good
345      than a 1024x1024 animation that looks really pixelated.
346    */
347   if (size > target_size)
348     scale *= target_size / size;
349
350   glScalef (scale, scale, scale);
351 }
352
353
354 static void
355 set_colors (const GLfloat *color)
356 {
357   glMaterialfv(GL_FRONT, GL_AMBIENT,   color + 0);
358   glMaterialfv(GL_FRONT, GL_DIFFUSE,   color + 4);
359   glMaterialfv(GL_FRONT, GL_SPECULAR,  color + 8);
360   glMaterialfv(GL_FRONT, GL_SHININESS, color + 12);
361 }
362
363 static void
364 set_colors_alpha (const GLfloat *color, GLfloat a)
365 {
366   GLfloat c[countof(leviathan_color)];
367   memcpy (c, color, sizeof(c));
368   c[3] = c[7] = c[11] = a;
369   set_colors (c);
370 }
371
372
373 static void
374 which_face (ModeInfo *mi, const GLfloat *f, int *face, int *outerp)
375 {
376   GLfloat size = 3;          /* 3" square */
377   const GLfloat *n = f;      /* normal */
378   const GLfloat *v = f + 3;  /* vertex */
379   GLfloat slack = 0.01;
380
381   /* First look at the normal to determine which direction this triangle
382      is facing (or is most-closely facing).
383      It's an outer surface if it is within epsilon of the cube wall that
384      it is facing.  Otherwise, it's an inner surface.
385    */
386   if      (n[1] < -0.5)   *face = 1, *outerp = v[1] < slack;       /* S */
387   else if (n[2] >  0.5)   *face = 2, *outerp = v[2] > size-slack;  /* U */
388   else if (n[1] >  0.5)   *face = 3, *outerp = v[1] > size-slack;  /* N */
389   else if (n[2] < -0.5)   *face = 4, *outerp = v[2] < slack;       /* D */
390   else if (n[0] < -0.5)   *face = 5, *outerp = v[0] < slack;       /* W */
391   else /* (n[0] >  0.5)*/ *face = 6, *outerp = v[0] > size-slack;  /* E */
392
393   /* Faces that don't have normals parallel to the axes aren't external. */
394   if (*outerp &&
395       (n[0] > -0.95 && n[0] < 0.95 &&
396        n[1] > -0.95 && n[1] < 0.95 &&
397        n[2] > -0.95 && n[2] < 0.95))
398     *outerp = 0;
399 }
400
401
402 static void
403 texturize_vert (ModeInfo *mi, int which, const GLfloat *v)
404 {
405   GLfloat size = 3;          /* 3" square */
406   GLfloat s = 0, q = 0;
407
408   /* Texture coordinates are surface coordinates,
409      on the plane of this cube wall. */
410   switch (which) {
411   case 0: break;
412   case 1: s = v[0], q = v[2]; break;
413   case 2: s = v[0], q = v[1]; break;
414   case 3: s = v[0], q = v[2]; q = size - q; break;
415   case 4: s = v[0], q = v[1]; q = size - q; break;
416   case 5: s = v[1], q = v[2]; break;
417   case 6: s = v[1], q = v[2]; break;
418   default: abort(); break;
419   }
420
421   glTexCoord2f (s / size, q / size);
422 }
423
424
425 static void
426 leviathan (ModeInfo *mi, GLfloat ratio, GLfloat alpha, Bool top_p)
427 {
428   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
429   Bool wire = MI_IS_WIREFRAME(mi);
430   GLfloat r = 0.34;
431   GLfloat z = 2 * ratio;
432   XYZ p[3];
433   int i;
434
435   GLfloat th = acos (2 / sqrt (6));  /* Line up with cube's diagonal */
436
437   glPushMatrix();
438
439   glRotatef (-45, 0, 1, 0);
440   glRotatef (-th * 180 / M_PI, 0, 0, 1);
441
442   if (!top_p)
443     glRotatef (180, 0, 0, 1);
444
445   for (i = 0; i < countof(p); i++)
446     {
447       GLfloat th = i * M_PI * 2 / countof(p);
448       p[i].x = cos(th) * r;
449       p[i].y = sin(th) * r;
450     }
451
452   glFrontFace (GL_CCW);
453   for (i = 0; i < countof(p); i++)
454     {
455       int j = (i + 1) % countof(p);
456 /*      if (top_p)*/
457         do_normal (z, 0, 0,
458                    0, p[i].x, p[i].y,
459                    0, p[j].x, p[j].y);
460 /*
461       else
462         do_normal (z, 0, 0,
463                    0, p[j].y, p[j].z,
464                    0, p[i].y, p[i].z);
465 */
466
467       if (do_texture)  /* Leviathan is the final texture */
468         glBindTexture (GL_TEXTURE_2D, lc->texids[countof(lc->texids) - 1]);
469
470       set_colors (leviathan_color);
471
472       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
473       glTexCoord2f (0.5, 1);
474       glVertex3f (z, 0, 0);
475
476       glTexCoord2f (0, 0);
477       glVertex3f (0, p[i].x, p[i].y);
478
479       glTexCoord2f (1, 0);
480       glVertex3f (0, p[j].x, p[j].y);
481       glEnd();
482       mi->polygon_count++;
483
484       /* Shield for fading */
485       if (alpha < 0.9 && !wire)
486         {
487           GLfloat a = 0.35;
488           GLfloat b = 0.69;
489
490           set_colors_alpha (black_color, 1-alpha);
491           glBindTexture (GL_TEXTURE_2D, 0);
492           if (!wire)
493             {
494               glEnable (GL_BLEND);
495               glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
496             }
497
498           glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
499
500           glVertex3f (z*a, p[j].x * b, p[j].y * b);
501           glVertex3f (z*a, p[i].x * b, p[i].y * b);
502           glVertex3f (0, p[i].x * 1.01, p[i].y * 1.01);
503           glVertex3f (0, p[j].x * 1.01, p[j].y * 1.01);
504           glEnd();
505           mi->polygon_count++;
506           glDisable (GL_BLEND);
507         }
508     }
509
510   glPopMatrix();
511 }
512
513
514 static void
515 folding_walls (ModeInfo *mi, GLfloat ratio, Bool top_p)
516 {
517   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
518   Bool wire = MI_IS_WIREFRAME(mi);
519   const GLfloat pa[4][2] = {{ -0.5,      -0.215833 },
520                             {  0,         0.5      },
521                             {  0.5,       0        },
522                             { -0.215833, -0.5      }};
523   const int tex[6] = { 0, 5, 1,  4, 2, 3 };
524   const GLfloat top = -pa[0][1];
525   GLfloat end_angle = 30.85;
526   GLfloat rr = sin (ratio / 2 * M_PI);
527   GLfloat offa = 0.15 * rr;
528   GLfloat offb = 0.06 * rr;
529   GLfloat p[4][3];
530   GLfloat t[4][2];
531   int i;
532
533   glPushMatrix();
534
535   if (top_p)
536     {
537       glRotatef (60, 1, -1, 1);
538       glRotatef (180, 0, 1, 0);
539       glRotatef (90, 1, 0, 0);
540     }
541   else
542     {
543       glRotatef (180, 1, 0, 0);
544     }
545
546   /* Scale down the points near the axis */
547
548   p[0][0] = pa[0][0];
549   p[0][1] = 0.5;
550   p[0][2] = pa[0][1];
551
552   p[1][0] = pa[1][0] - offb;
553   p[1][1] = 0.5;
554   p[1][2] = pa[1][1] - offa;
555
556   p[2][0] = pa[2][0] - offa;
557   p[2][1] = 0.5;
558   p[2][2] = pa[2][1] - offb;
559
560   p[3][0] = pa[3][0];
561   p[3][1] = 0.5;
562   p[3][2] = pa[3][1];
563
564   if (!wire)
565     {
566       glEnable (GL_BLEND);
567       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
568     }
569
570   for (i = 0; i < 3; i++)
571     {
572       glPushMatrix();
573
574       if (i == 1)
575         {
576           glRotatef (-90, 1, 0, 0);
577           glRotatef (180, 1, 1, 0);
578         }
579       else if (i == 2)
580         {
581           glRotatef (-90, 1, 0, 0);
582           glRotatef (180, 0, 1, 0);
583           glRotatef (90, 0, 1, 0);
584         }
585
586       glRotatef (-90, 0, 1, 0);
587
588       glTranslatef (-(top/2 + 0.25), 0.5, -(top/2 + 0.25));
589       glRotatef (-45, 0, 1, 0);
590       glRotatef (ratio * -end_angle, 0, 0, 1);
591       glRotatef (45, 0, 1, 0);
592       glTranslatef (top/2 + 0.25, -0.5, top/2 + 0.25);
593
594       /* Get the texture coordinates right.
595          This is hairy and incomprehensible. */
596
597       t[0][0] = pa[0][1] + 0.5; t[0][1] = pa[0][0] + 0.5;
598       t[1][0] = pa[1][1] + 0.5; t[1][1] = pa[1][0] + 0.5;
599       t[2][0] = pa[2][1] + 0.5; t[2][1] = pa[2][0] + 0.5;
600       t[3][0] = pa[3][1] + 0.5; t[3][1] = pa[3][0] + 0.5;
601
602       if (i == 0 && !top_p)
603         {
604 # define SWAP(A,B) A = 1-A, B = 1-B
605           SWAP(t[0][0], t[0][1]);
606           SWAP(t[1][0], t[1][1]);
607           SWAP(t[2][0], t[2][1]);
608           SWAP(t[3][0], t[3][1]);
609 # undef SWAP
610         }
611       else if (i == 0 && top_p)
612         {
613           GLfloat ot[4][3];
614           memcpy (ot, t, sizeof(t));
615 # define SWAP(A,B) A = 1-A, B = 1-B
616           SWAP(t[0][0], ot[2][1]);
617           SWAP(t[1][0], ot[3][1]);
618           SWAP(t[2][0], ot[0][1]);
619           SWAP(t[3][0], ot[1][1]);
620 # undef SWAP
621         }
622       else if (i == 1)
623         {
624           GLfloat f;
625 # define SWAP(A,B) f = A, A = B, B = -f
626           SWAP(t[0][0], t[0][1]);
627           SWAP(t[1][0], t[1][1]);
628           SWAP(t[2][0], t[2][1]);
629           SWAP(t[3][0], t[3][1]);
630 # undef SWAP
631         }
632
633       set_colors_alpha (exterior_color, 1-ratio);
634       glBindTexture (GL_TEXTURE_2D, lc->texids[tex[i + (top_p ? 3 : 0)]]);
635
636       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
637       do_normal (p[0][0], p[0][1], p[0][2],
638                  p[1][0], p[1][1], p[1][2],
639                  p[2][0], p[2][1], p[2][2]);
640       glTexCoord2fv(t[0]); glVertex3fv(p[0]);
641       glTexCoord2fv(t[1]); glVertex3fv(p[1]);
642       glTexCoord2fv(t[2]); glVertex3fv(p[2]);
643       glTexCoord2fv(t[3]); glVertex3fv(p[3]);
644       glEnd();
645       mi->polygon_count++;
646
647       /* The triangles between the quads */
648 # if 0
649       /* #### There is a fucking gap between the two black triangles 
650          that I can't figure out!  So instead of drawing the triangles,
651          we build a black shield around the middle bit in leviathan()
652          and count on back-face culling to have roughly the same effect.
653        */
654       if (!wire)
655         {
656           GLfloat pp[4][3];
657           memcpy (pp, p, sizeof(pp));
658           memcpy (pp[2], pp[1], sizeof(pp[1]));
659           pp[2][0] -= 0.5 * (1-ratio);
660           pp[2][1] -= 0.5 * (1-ratio);
661
662           glBindTexture (GL_TEXTURE_2D, 0);
663           set_colors_alpha (black_color, 1-ratio);
664
665           glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLES);
666           do_normal (pp[0][0], pp[0][1], pp[0][2],
667                      pp[2][0], pp[2][1], pp[2][2],
668                      pp[1][0], pp[1][1], pp[1][2]);
669           glVertex3fv(pp[0]);
670           glVertex3fv(pp[2]);
671           glVertex3fv(pp[1]);
672           glEnd();
673           mi->polygon_count++;
674         }
675 # endif
676
677       glPopMatrix();
678     }
679
680   if (! wire) glDisable (GL_BLEND);
681
682   glPopMatrix();
683 }
684
685
686 static int
687 lament_sphere (ModeInfo *mi, GLfloat ratio)
688 {
689   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
690   Bool wire = MI_IS_WIREFRAME(mi);
691   GLfloat size = 3; /* 3" square */
692   int polys = 0;
693   int facets = 16;  /* NxN grid on each face */
694   int face;
695   static const GLfloat norms[6][3] = {{ 0, -1, 0 }, {  0, 0, 1 }, { 0, 1, 0 },
696                                       { 0, 0, -1 }, { -1, 0, 0 }, { 1, 0, 0 }};
697   GLfloat s = 1.0 / facets;
698
699   /* The ratio used for the normals: linger on the square normals. */
700   GLfloat ratio2 = 1 - sin ((1 - ratio) / 2 * M_PI);
701   GLfloat r1 = 1 - ratio2 / 2;
702   GLfloat r2 =     ratio2 / 2;
703
704   glPushMatrix();
705   glTranslatef (-0.5, -0.5, -0.5);
706   glScalef (1/size, 1/size, 1/size);
707
708   set_colors (exterior_color);
709
710   for (face = 0; face < 6; face++)
711     {
712       GLfloat x0, y0;
713       for (y0 = 0; y0 < 1; y0 += s)
714         for (x0 = 0; x0 < 1; x0 += s)
715         {
716           int i;
717           GLfloat x1 = x0 + s;
718           GLfloat y1 = y0 + s;
719           GLfloat pa[4][3];     /* verts of the cube */
720           GLfloat pb[4][3];     /* verts of the transition to the sphere */
721           Bool frontp;
722           GLfloat norm[4][3];   /* normals of the transitional verts */
723
724           if (norms[face][0])
725             frontp = norms[face][0] < 0,
726             pa[0][1] = x0, pa[0][2] = y0, pa[0][0] = (frontp ? 0 : 1),
727             pa[1][1] = x1, pa[1][2] = y0, pa[1][0] = pa[0][0],
728             pa[2][1] = x1, pa[2][2] = y1, pa[2][0] = pa[0][0],
729             pa[3][1] = x0, pa[3][2] = y1, pa[3][0] = pa[0][0];
730           else if (norms[face][1])
731             frontp = norms[face][1] > 0,
732             pa[0][0] = x0, pa[0][2] = y0, pa[0][1] = (frontp ? 1 : 0),
733             pa[1][0] = x1, pa[1][2] = y0, pa[1][1] = pa[0][1],
734             pa[2][0] = x1, pa[2][2] = y1, pa[2][1] = pa[0][1],
735             pa[3][0] = x0, pa[3][2] = y1, pa[3][1] = pa[0][1];
736           else /* (norms[face][2]) */
737             frontp = norms[face][2] < 0,
738             pa[0][0] = x0, pa[0][1] = y0, pa[0][2] = (frontp ? 0 : 1),
739             pa[1][0] = x1, pa[1][1] = y0, pa[1][2] = pa[0][2],
740             pa[2][0] = x1, pa[2][1] = y1, pa[2][2] = pa[0][2],
741             pa[3][0] = x0, pa[3][1] = y1, pa[3][2] = pa[0][2];
742
743           for (i = 0; i < countof(pa); i++)
744             pa[i][0] *= size, pa[i][1] *= size, pa[i][2] *= size;
745
746           /* Convert square to sphere by treating as a normalized vector */
747           for (i = 0; i < countof(pa); i++)
748             {
749               GLfloat x = (pa[i][0] / size) - 0.5;
750               GLfloat y = (pa[i][1] / size) - 0.5;
751               GLfloat z = (pa[i][2] / size) - 0.5;
752               GLfloat d = sqrt (x*x + y*y + z*z) / 2;
753               x = x/d + size/2;
754               y = y/d + size/2;
755               z = z/d + size/2;
756
757               pb[i][0] = pa[i][0] + ((x - pa[i][0]) * ratio);
758               pb[i][1] = pa[i][1] + ((y - pa[i][1]) * ratio);
759               pb[i][2] = pa[i][2] + ((z - pa[i][2]) * ratio);
760             }
761
762           /* The normals of an intermediate point are the weighted average
763              of the cube's orthogonal normals, and the sphere's radial
764              normals: early in the sequence, the edges are sharp, but they
765              soften as it expands. */
766           {
767             XYZ na, pa0, pa1, pa2;
768             pa0.x = pa[0][0]; pa0.y = pa[0][1]; pa0.z = pa[0][2];
769             pa1.x = pa[1][0]; pa1.y = pa[1][1]; pa1.z = pa[1][2];
770             pa2.x = pa[2][0]; pa2.y = pa[2][1]; pa2.z = pa[2][2];
771             na = calc_normal (pa0, pa1, pa2);
772
773             for (i = 0; i < countof(pb); i++)
774               {
775                 GLfloat d;
776                 XYZ nb;
777
778                 nb.x = pb[i][0];
779                 nb.y = pb[i][1];
780                 nb.z = pb[i][2];
781                 d = sqrt (nb.x*nb.x + nb.y*nb.y + nb.z*nb.z);  /* normalize */
782                 nb.x /= d;
783                 nb.y /= d;
784                 nb.z /= d;
785
786                 norm[i][0] = (na.x * r1) + (nb.x * r2);  /* weighted */
787                 norm[i][1] = (na.y * r1) + (nb.y * r2);
788                 norm[i][2] = (na.z * r1) + (nb.z * r2);
789               }
790           }
791
792           if (! wire)
793             glBindTexture (GL_TEXTURE_2D, lc->texids[face]);
794
795           glFrontFace (frontp ? GL_CW : GL_CCW);
796           glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
797
798           texturize_vert (mi, face+1, pa[0]);
799           glNormal3fv (norm[0]);
800           glVertex3fv (pb[0]);
801
802           texturize_vert (mi, face+1, pa[1]);
803           glNormal3fv (norm[1]);
804           glVertex3fv (pb[1]);
805
806           texturize_vert (mi, face+1, pa[2]);
807           glNormal3fv (norm[2]);
808           glVertex3fv (pb[2]);
809
810           texturize_vert (mi, face+1, pa[3]);
811           glNormal3fv (norm[3]);
812           glVertex3fv (pb[3]);
813
814           glEnd();
815           polys++;
816         }
817     }
818
819   glPopMatrix();
820
821   return polys;
822 }
823
824
825 static void
826 draw (ModeInfo *mi)
827 {
828   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
829   Bool wire = MI_IS_WIREFRAME(mi);
830
831   mi->polygon_count = 0;
832
833   if (!wire)
834     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
835   else
836     glClear(GL_COLOR_BUFFER_BIT);
837
838   glPushMatrix();
839
840   gltrackball_rotate (lc->trackball);
841
842   /* Make into the screen be +Y, right be +X, and up be +Z. */
843   glRotatef (-90.0, 1.0, 0.0, 0.0);
844
845   scale_for_window (mi);
846
847   /* Apply rotation to the object. */
848   if (lc->type != LAMENT_LID_ZOOM)
849     get_rotation (lc->rot, &lc->rotx, &lc->roty, &lc->rotz,
850                   !lc->button_down_p);
851 # ifdef DEBUG_MODE
852   lc->rotx = 0.18;
853   lc->roty = 0.22;
854   lc->rotx = lc->roty = 0;
855   lc->rotz = 0;
856 # endif
857
858   glRotatef (lc->rotx * 360, 1, 0, 0);
859   glRotatef (lc->roty * 360, 0, 1, 0);
860   glRotatef (lc->rotz * 360, 0, 0, 1);
861
862   glScalef (0.5, 0.5, 0.5);
863
864   switch (lc->type)
865     {
866     case LAMENT_BOX:
867       glCallList (lc->dlists[OBJ_BOX]);
868       mi->polygon_count += lc->polys[OBJ_BOX];
869       break;
870
871     case LAMENT_STAR_OUT:
872     case LAMENT_STAR_ROT:
873     case LAMENT_STAR_ROT_IN:
874     case LAMENT_STAR_ROT_OUT:
875     case LAMENT_STAR_UNROT:
876     case LAMENT_STAR_IN:
877       glTranslatef (0.0, 0.0, lc->anim_z/2);
878       glRotatef (lc->anim_r/2, 0.0, 0.0, 1.0);
879       glCallList (lc->dlists[OBJ_STAR_U]);
880       mi->polygon_count += lc->polys[OBJ_STAR_U];
881
882       glTranslatef (0.0, 0.0, -lc->anim_z);
883       glRotatef (-lc->anim_r, 0.0, 0.0, 1.0);
884       glCallList (lc->dlists[OBJ_STAR_D]);
885       mi->polygon_count += lc->polys[OBJ_STAR_D];
886       break;
887
888     case LAMENT_TETRA_UNE:
889     case LAMENT_TETRA_USW:
890     case LAMENT_TETRA_DWN:
891     case LAMENT_TETRA_DSE:
892       {
893         int magic;
894         GLfloat x, y, z;
895         switch (lc->type) {
896         case LAMENT_TETRA_UNE: magic = OBJ_TETRA_UNE; x= 1; y= 1; z= 1; break;
897         case LAMENT_TETRA_USW: magic = OBJ_TETRA_USW; x= 1; y= 1; z=-1; break;
898         case LAMENT_TETRA_DWN: magic = OBJ_TETRA_DWN; x= 1; y=-1; z= 1; break;
899         case LAMENT_TETRA_DSE: magic = OBJ_TETRA_DSE; x=-1; y= 1; z= 1; break;
900         default: abort(); break;
901         }
902         glCallList(lc->dlists[OBJ_TETRA_BASE]);
903         mi->polygon_count += lc->polys[OBJ_TETRA_BASE];
904         if (magic != OBJ_TETRA_UNE) glCallList (lc->dlists[OBJ_TETRA_UNE]);
905         if (magic != OBJ_TETRA_USW) glCallList (lc->dlists[OBJ_TETRA_USW]);
906         if (magic != OBJ_TETRA_DWN) glCallList (lc->dlists[OBJ_TETRA_DWN]);
907         if (magic != OBJ_TETRA_DSE) glCallList (lc->dlists[OBJ_TETRA_DSE]);
908         glRotatef (lc->anim_r, x, y, z);
909         glCallList (lc->dlists[magic]);
910         mi->polygon_count += lc->polys[magic] * 3;
911       }
912       break;
913
914     case LAMENT_LID_OPEN:
915     case LAMENT_LID_CLOSE:
916     case LAMENT_LID_ZOOM:
917       {
918         GLfloat d = 0.21582;
919         int i;
920         const int lists[4] = { OBJ_LID_A, OBJ_LID_B, OBJ_LID_C, OBJ_LID_D };
921
922         lc->facing_p = facing_screen_p (mi);
923
924         if (lc->anim_z < 0.5)
925           glTranslatef (0, -30 * lc->anim_z, 0);    /* zoom */
926         else
927           glTranslatef (8 * (0.5 - (lc->anim_z - 0.5)), 0, 0);
928
929         glCallList (lc->dlists[OBJ_LID_BASE]);
930         mi->polygon_count += lc->polys[OBJ_LID_BASE];
931         for (i = 0; i < countof(lists); i++)
932           {
933             glPushMatrix();
934             glRotatef (90 * i, 0, 1, 0);
935             glTranslatef (-d, -0.5, d);
936             glRotatef (-45, 0, 1, 0);
937             glRotatef (-lc->anim_r, 1, 0, 0);
938             glRotatef (45, 0, 1, 0);
939             glTranslatef (d, 0.5, -d);
940             glRotatef (-90 * i, 0, 1, 0);
941             glCallList (lc->dlists[lists[i]]);
942             mi->polygon_count += lc->polys[lists[i]];
943             glPopMatrix();
944           }
945
946 # ifdef DEBUG_MODE
947         if (lc->facing_p)
948           {
949             glColor3f(1, 0, 0);
950             glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
951             glVertex3f (-0.49, 0.49, -0.49);
952             glVertex3f ( 0.49, 0.49, -0.49);
953             glVertex3f ( 0.49, 0.49,  0.49);
954             glVertex3f (-0.49, 0.49,  0.49);
955             glEnd();
956             mi->polygon_count++;
957           }
958 # endif /* DEBUG_MODE */
959       }
960       break;
961
962     case LAMENT_TASER_OUT:
963     case LAMENT_TASER_SLIDE:
964     case LAMENT_TASER_SLIDE_IN:
965     case LAMENT_TASER_IN:
966
967       glTranslatef (0, -lc->anim_z/2, 0);
968       glCallList (lc->dlists[OBJ_TASER_BASE]);
969       mi->polygon_count += lc->polys[OBJ_TASER_BASE];
970
971       glTranslatef (0, lc->anim_z, 0);
972       glCallList (lc->dlists[OBJ_TASER_A]);
973       mi->polygon_count += lc->polys[OBJ_TASER_A];
974
975       glTranslatef (lc->anim_y, 0, 0);
976       glCallList (lc->dlists[OBJ_TASER_B]);
977       mi->polygon_count += lc->polys[OBJ_TASER_B];
978       break;
979
980     case LAMENT_PILLAR_OUT:
981     case LAMENT_PILLAR_SPIN:
982     case LAMENT_PILLAR_IN:
983
984       glCallList (lc->dlists[OBJ_PILLAR_BASE]);
985       mi->polygon_count += lc->polys[OBJ_PILLAR_BASE];
986
987       glPushMatrix();
988       if (lc->anim_z == 1 || lc->anim_z == 3)
989         {
990           glRotatef (lc->anim_r, 0, 0, 1);
991           glTranslatef (0, 0, lc->anim_y);
992         }
993       glCallList (lc->dlists[OBJ_PILLAR_A]);
994       mi->polygon_count += lc->polys[OBJ_PILLAR_A];
995       glPopMatrix();
996
997       glPushMatrix();
998       if (lc->anim_z == 2 || lc->anim_z == 3)
999         {
1000           glRotatef (lc->anim_r, 0, 0, 1);
1001           glTranslatef (0, 0, -lc->anim_y);
1002         }
1003       glCallList (lc->dlists[OBJ_PILLAR_B]);
1004       mi->polygon_count += lc->polys[OBJ_PILLAR_B];
1005       glPopMatrix();
1006       break;
1007
1008     case LAMENT_SPHERE_OUT:
1009     case LAMENT_SPHERE_IN:
1010       mi->polygon_count += lament_sphere (mi, lc->anim_y);
1011       break;
1012
1013     case LAMENT_LEVIATHAN_SPIN:
1014     case LAMENT_LEVIATHAN_UNSPIN:
1015     case LAMENT_LEVIATHAN_FADE:
1016     case LAMENT_LEVIATHAN_UNFADE:
1017     case LAMENT_LEVIATHAN_TWIST:
1018     case LAMENT_LEVIATHAN_UNTWIST:
1019       {
1020         /* These normals are hard to compute, so I pulled them from the
1021            model. */
1022         const GLfloat axes[6][4] =
1023           {{ OBJ_ISO_UNE,  0.633994, 0.442836,   0.633994 },
1024            { OBJ_ISO_USW,  0.442836,  0.633994, -0.633994 },
1025            { OBJ_ISO_DSE, -0.633994,  0.633994,  0.442836 },
1026            { OBJ_ISO_SWD, -0.633994, -0.442836, -0.633994 },
1027            { OBJ_ISO_DEN, -0.442836, -0.633994,  0.633994 },
1028            { OBJ_ISO_UNW,  0.633994, -0.633994, -0.442836 }};
1029         int i;
1030
1031         GLfloat s = (1 - lc->anim_z);
1032         GLfloat s2 = MAX (0, 360 - lc->anim_r) / 360.0;
1033         Bool blendp = 0;
1034
1035         switch (lc->type) {
1036         case LAMENT_LEVIATHAN_SPIN: break;
1037         case LAMENT_LEVIATHAN_UNSPIN: s2 = 1 - s2; break;
1038         default: s2 = 0; blendp = 1; break;
1039         }
1040
1041         if (wire) blendp = 0;
1042
1043         s  = (s * 0.6) + 0.4;
1044
1045         leviathan (mi, 1 - s2, 1, True);
1046         glCallList (lc->dlists[OBJ_ISO_BASE_A]);
1047         mi->polygon_count += lc->polys[OBJ_ISO_BASE_A];
1048
1049         glPushMatrix();
1050         glScalef (s2, s2, s2);
1051         glCallList (lc->dlists[OBJ_ISO_USE]);
1052         mi->polygon_count += lc->polys[OBJ_ISO_USE];
1053         glPopMatrix();
1054
1055         glPushMatrix();
1056         glRotatef (lc->anim_y, 1, -1, 1);
1057         glCallList (lc->dlists[OBJ_ISO_BASE_B]);
1058         mi->polygon_count += lc->polys[OBJ_ISO_BASE_B];
1059         leviathan (mi, 1 - s2, 1, False);
1060         glPopMatrix();
1061
1062         if (blendp)
1063           {
1064 # ifndef HAVE_JWZGLES /* no glBlendColor */
1065             glEnable (GL_BLEND);
1066             glBlendFunc (GL_CONSTANT_ALPHA, GL_SRC_ALPHA);
1067             glBlendColor (1, 1, 1, MAX(0, 1-(lc->anim_z * 3)));
1068 # endif
1069           }
1070
1071         for (i = 0; i < countof(axes); i++)
1072           {
1073             glPushMatrix();
1074             glRotatef (lc->anim_r, axes[i][1], axes[i][2], axes[i][3]);
1075             glScalef (s, s, s);
1076             glCallList (lc->dlists[(int) axes[i][0]]);
1077             mi->polygon_count += lc->polys[(int) axes[i][0]];
1078             glPopMatrix();
1079             if (i == 2)
1080               glRotatef (lc->anim_y, 1, -1, 1);
1081           }
1082
1083         if (blendp) glDisable (GL_BLEND);
1084
1085         glPushMatrix();
1086         glScalef (s2, s2, s2);
1087         glCallList (lc->dlists[OBJ_ISO_DWN]);
1088         mi->polygon_count += lc->polys[OBJ_ISO_DWN];
1089         glPopMatrix();
1090       }
1091       break;
1092
1093     case LAMENT_LEVIATHAN_COLLAPSE:
1094     case LAMENT_LEVIATHAN_EXPAND:
1095       {
1096         glPushMatrix();
1097         leviathan (mi, 1, lc->anim_y, True);
1098         glRotatef (180, 1, -1, 1);
1099         leviathan (mi, 1, lc->anim_y, False);
1100         glPopMatrix();
1101         folding_walls (mi, lc->anim_y, True);
1102         folding_walls (mi, lc->anim_y, False);
1103       }
1104       break;
1105
1106     default:
1107       abort();
1108       break;
1109     }
1110
1111   glPopMatrix();
1112 }
1113
1114
1115 /* Rather than just picking states randomly, pick an ordering randomly, do it,
1116    and then re-randomize.  That way one can be assured of seeing all states in
1117    a short time period, though not always in the same order (it's frustrating
1118    to see it pick the same state 5x in a row.)  Though, that can still happen,
1119    since states are in the list multiple times as a way of giving them
1120    probabilities.
1121  */
1122 static void
1123 shuffle_states (lament_configuration *lc)
1124 {
1125   int i;
1126   for (i = 0; i < lc->nstates; i++)
1127     {
1128       int a = random() % lc->nstates;
1129       lament_type swap = lc->states[a];
1130       lc->states[a] = lc->states[i];
1131       lc->states[i] = swap;
1132     }
1133 }
1134
1135
1136 static void
1137 animate (ModeInfo *mi)
1138 {
1139   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1140   int pause = 10;
1141   int pause2 = 120;
1142   GLfloat speed = (lc->ffwdp ? 20 : 1);
1143
1144   switch (lc->type)
1145     {
1146     case LAMENT_BOX:
1147       {
1148         lc->state++;
1149         if (lc->state >= lc->nstates)
1150           {
1151             shuffle_states (lc);
1152             lc->state = 0;
1153           }
1154         lc->type = lc->states[lc->state];
1155
1156         if (lc->type == LAMENT_BOX)
1157           lc->anim_pause = pause2;
1158
1159         lc->anim_r = 0.0;
1160         lc->anim_y = 0.0;
1161         lc->anim_z = 0.0;
1162       }
1163       break;
1164
1165       /* -------------------------------------------------------------- */
1166
1167     case LAMENT_STAR_OUT:
1168       lc->anim_z += 0.01 * speed;
1169       if (lc->anim_z >= 1.0)
1170         {
1171           lc->anim_z = 1.0;
1172           lc->type = LAMENT_STAR_ROT;
1173           lc->anim_pause = pause;
1174         }
1175       break;
1176
1177     case LAMENT_STAR_ROT:
1178       lc->anim_r += 1.0 * speed;
1179       if (lc->anim_r >= 45.0)
1180         {
1181           lc->anim_r = 45.0;
1182           lc->type = LAMENT_STAR_ROT_IN;
1183           lc->anim_pause = pause;
1184         }
1185       break;
1186
1187     case LAMENT_STAR_ROT_IN:
1188       lc->anim_z -= 0.01 * speed;
1189       if (lc->anim_z <= 0.0)
1190         {
1191           lc->anim_z = 0.0;
1192           lc->type = LAMENT_STAR_ROT_OUT;
1193           lc->anim_pause = pause2 * (1 + frand(2) + frand(2));
1194         }
1195       break;
1196
1197     case LAMENT_STAR_ROT_OUT:
1198       lc->anim_z += 0.01 * speed;
1199       if (lc->anim_z >= 1.0)
1200         {
1201           lc->anim_z = 1.0;
1202           lc->type = LAMENT_STAR_UNROT;
1203           lc->anim_pause = pause;
1204         }
1205       break;
1206       
1207     case LAMENT_STAR_UNROT:
1208       lc->anim_r -= 1.0 * speed;
1209       if (lc->anim_r <= 0.0)
1210         {
1211           lc->anim_r = 0.0;
1212           lc->type = LAMENT_STAR_IN;
1213           lc->anim_pause = pause;
1214         }
1215       break;
1216
1217     case LAMENT_STAR_IN:
1218       lc->anim_z -= 0.01 * speed;
1219       if (lc->anim_z <= 0.0)
1220         {
1221           lc->anim_z = 0.0;
1222           lc->type = LAMENT_BOX;
1223           lc->anim_pause = pause2;
1224         }
1225       break;
1226
1227       /* -------------------------------------------------------------- */
1228
1229     case LAMENT_TETRA_UNE:
1230     case LAMENT_TETRA_USW:
1231     case LAMENT_TETRA_DWN:
1232     case LAMENT_TETRA_DSE:
1233
1234       lc->anim_r += 1.0 * speed;
1235       if (lc->anim_r >= 360.0)
1236         {
1237           lc->anim_r = 0.0;
1238           lc->type = LAMENT_BOX;
1239           lc->anim_pause = pause2;
1240         }
1241       else if (lc->anim_r > 119.0 && lc->anim_r <= 120.0)
1242         {
1243           lc->anim_r = 120.0;
1244           lc->anim_pause = pause;
1245         }
1246       else if (lc->anim_r > 239.0 && lc->anim_r <= 240.0)
1247         {
1248           lc->anim_r = 240.0;
1249           lc->anim_pause = pause;
1250         }
1251       break;
1252
1253       /* -------------------------------------------------------------- */
1254
1255     case LAMENT_LID_OPEN:
1256       lc->anim_r += 1.0 * speed;
1257
1258       if (lc->anim_r >= 112.0)
1259         {
1260           lc->anim_r = 112.0;
1261           lc->anim_z = 0.0;
1262           lc->anim_pause = pause2;
1263           lc->type = (lc->facing_p ? LAMENT_LID_ZOOM : LAMENT_LID_CLOSE);
1264         }
1265       break;
1266
1267     case LAMENT_LID_CLOSE:
1268       lc->anim_r -= 1.0 * speed;
1269       if (lc->anim_r <= 0.0)
1270         {
1271           lc->anim_r = 0.0;
1272           lc->type = LAMENT_BOX;
1273           lc->anim_pause = pause2;
1274         }
1275       break;
1276
1277     case LAMENT_LID_ZOOM:
1278       lc->anim_z += 0.01 * speed;
1279       if (lc->anim_z > 1.0)
1280         {
1281           lc->anim_r = 0.0;
1282           lc->anim_z = 0.0;
1283           lc->type = LAMENT_BOX;
1284         }
1285       break;
1286
1287       /* -------------------------------------------------------------- */
1288
1289     case LAMENT_TASER_OUT:
1290       lc->anim_z += 0.005 * speed;
1291       if (lc->anim_z >= 0.5)
1292         {
1293           lc->anim_z = 0.5;
1294           lc->type = LAMENT_TASER_SLIDE;
1295           lc->anim_pause = pause * (1 + frand(5) + frand(5));
1296         }
1297       break;
1298
1299     case LAMENT_TASER_SLIDE:
1300       lc->anim_y += 0.005 * speed;
1301       if (lc->anim_y >= 0.255)
1302         {
1303           lc->anim_y = 0.255;
1304           lc->type = LAMENT_TASER_SLIDE_IN;
1305           lc->anim_pause = pause2 * (1 + frand(5) + frand(5));
1306         }
1307       break;
1308
1309     case LAMENT_TASER_SLIDE_IN:
1310       lc->anim_y -= 0.0025 * speed;
1311       if (lc->anim_y <= 0.0)
1312         {
1313           lc->anim_y = 0.0;
1314           lc->type = LAMENT_TASER_IN;
1315           lc->anim_pause = pause;
1316         }
1317       break;
1318
1319     case LAMENT_TASER_IN:
1320       lc->anim_z -= 0.0025 * speed;
1321       if (lc->anim_z <= 0.0)
1322         {
1323           lc->anim_z = 0.0;
1324           lc->type = LAMENT_BOX;
1325           lc->anim_pause = pause2;
1326         }
1327       break;
1328
1329       /* -------------------------------------------------------------- */
1330
1331     case LAMENT_PILLAR_OUT:
1332
1333       if (lc->anim_y == 0)  /* mostly in */
1334         lc->anim_y += 0.005 * ((random() % 5) ? -1 : 1) * speed;
1335       else if (lc->anim_y > 0)
1336         lc->anim_y += 0.005 * speed;
1337       else
1338         lc->anim_y -= 0.001 * speed;
1339
1340       if (lc->anim_z == 0)
1341         {
1342           int i = (random() % 7);   /* A, B or both */
1343           if      (i == 0) lc->anim_z = 3;
1344           else if (i < 5)  lc->anim_z = 2;
1345           else             lc->anim_z = 1;
1346
1347           /* We can do quarter turns, because it's radially symmetrical. */
1348           lc->anim_r =  90.0 * (1 + frand(6)) * RANDSIGN();
1349         }
1350       if (lc->anim_y > 0.4)
1351         {
1352           lc->anim_y = 0.4;
1353           lc->type = LAMENT_PILLAR_SPIN;
1354           lc->anim_pause = pause;
1355         }
1356       else if (lc->anim_y < -0.03)
1357         {
1358           lc->anim_y = -0.03;
1359           lc->type = LAMENT_PILLAR_SPIN;
1360           lc->anim_pause = pause;
1361         }
1362       break;
1363
1364     case LAMENT_PILLAR_SPIN:
1365       {
1366         Bool negp = (lc->anim_r < 0);
1367         lc->anim_r += (negp ? 1 : -1) * speed;
1368         if (negp ? lc->anim_r > 0 : lc->anim_r < 0)
1369           {
1370             lc->anim_r = 0;
1371             lc->type = LAMENT_PILLAR_IN;
1372           }
1373       }
1374       break;
1375
1376     case LAMENT_PILLAR_IN:
1377       {
1378         Bool negp = (lc->anim_y < 0);
1379         lc->anim_y += (negp ? 1 : -1) * 0.005 * speed;
1380         if (negp ? lc->anim_y > 0 : lc->anim_y < 0)
1381           {
1382             lc->anim_y = 0;
1383             lc->anim_z = 0;
1384             lc->type = LAMENT_BOX;
1385             lc->anim_pause = pause;
1386           }
1387       }
1388       break;
1389
1390       /* -------------------------------------------------------------- */
1391
1392     case LAMENT_SPHERE_OUT:
1393       {
1394         lc->anim_y += 0.01 * speed;
1395         if (lc->anim_y >= 1)
1396           {
1397             lc->anim_y = 1;
1398             lc->type = LAMENT_SPHERE_IN;
1399             lc->anim_pause = pause2 * (1 + frand(1) + frand(1));
1400           }
1401       }
1402       break;
1403
1404     case LAMENT_SPHERE_IN:
1405       {
1406         lc->anim_y -= 0.01 * speed;
1407         if (lc->anim_y <= 0)
1408           {
1409             lc->anim_y = 0;
1410             lc->type = LAMENT_BOX;
1411             lc->anim_pause = pause;
1412           }
1413       }
1414       break;
1415
1416       /* -------------------------------------------------------------- */
1417
1418     case LAMENT_LEVIATHAN_SPIN:
1419       lc->anim_r += 3.5 * speed;
1420       if (lc->anim_r >= 360 * 3)
1421         {
1422           lc->anim_r = 0;
1423           lc->type = LAMENT_LEVIATHAN_FADE;
1424           lc->anim_pause = 0;
1425         }
1426       break;
1427
1428     case LAMENT_LEVIATHAN_FADE:
1429       lc->anim_z += 0.01 * speed;
1430       if (lc->anim_z >= 1)
1431         {
1432           lc->anim_z = 1;
1433           lc->type = LAMENT_LEVIATHAN_TWIST;
1434           lc->anim_pause = 0;
1435         }
1436       break;
1437
1438     case LAMENT_LEVIATHAN_TWIST:
1439       lc->anim_y += 2 * speed;
1440       lc->anim_z = 1;
1441       if (lc->anim_y >= 180)
1442         {
1443           lc->anim_y = 0;
1444           lc->type = LAMENT_LEVIATHAN_COLLAPSE;
1445           lc->anim_pause = 0;
1446         }
1447       break;
1448
1449     case LAMENT_LEVIATHAN_COLLAPSE:
1450       lc->anim_y += 0.01 * speed;
1451       if (lc->anim_y >= 1)
1452         {
1453           lc->anim_y = 1.0;
1454           lc->type = LAMENT_LEVIATHAN_EXPAND;
1455           lc->anim_pause = pause2 * 4;
1456         }
1457       break;
1458
1459     case LAMENT_LEVIATHAN_EXPAND:
1460       lc->anim_y -= 0.005 * speed;
1461       if (lc->anim_y <= 0)
1462         {
1463           lc->anim_y = 180;
1464           lc->type = LAMENT_LEVIATHAN_UNTWIST;
1465         }
1466       break;
1467
1468     case LAMENT_LEVIATHAN_UNTWIST:
1469       lc->anim_y -= 2 * speed;
1470       lc->anim_z = 1;
1471       if (lc->anim_y <= 0)
1472         {
1473           lc->anim_y = 0;
1474           lc->type = LAMENT_LEVIATHAN_UNFADE;
1475           lc->anim_pause = 0;
1476         }
1477       break;
1478
1479     case LAMENT_LEVIATHAN_UNFADE:
1480       lc->anim_z -= 0.1 * speed;
1481       if (lc->anim_z <= 0)
1482         {
1483           lc->anim_z = 0;
1484           lc->type = LAMENT_LEVIATHAN_UNSPIN;
1485           lc->anim_pause = 0;
1486         }
1487       break;
1488
1489     case LAMENT_LEVIATHAN_UNSPIN:
1490       lc->anim_r += 3.5 * speed;
1491       if (lc->anim_r >= 360 * 2)
1492         {
1493           lc->anim_r = 0;
1494           lc->type = LAMENT_BOX;
1495           lc->anim_pause = pause2;
1496         }
1497       break;
1498
1499     default:
1500       abort();
1501       break;
1502     }
1503
1504 # ifdef DEBUG_MODE
1505
1506   lc->anim_pause = 0;
1507
1508   if (lc->type == LAMENT_BOX)
1509     lc->type = DEBUG_MODE;
1510
1511   if (lc->ffwdp)
1512     {
1513       lc->ffwdp = 0;
1514       while (lc->type != DEBUG_MODE)
1515         animate (mi);
1516     }
1517
1518 # else /* !DEBUG_MODE */
1519
1520   if (lc->ffwdp && lc->type == LAMENT_BOX)
1521     {
1522       lc->ffwdp = 0;
1523       while (lc->type == LAMENT_BOX)
1524         animate (mi);
1525       lc->anim_pause = 0;
1526     }
1527
1528 # endif /* !DEBUG_MODE */
1529 }
1530
1531
1532 static void
1533 gl_init (ModeInfo *mi)
1534 {
1535   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1536   Bool wire = MI_IS_WIREFRAME(mi);
1537   int i;
1538
1539   if (wire)
1540     do_texture = False;
1541
1542   if (!wire)
1543     {
1544       static const GLfloat pos0[]  = { -4.0,  2.0, 5.0, 1.0 };
1545       static const GLfloat pos1[]  = {  6.0, -1.0, 3.0, 1.0 };
1546
1547       static const GLfloat amb0[]  = { 0.7, 0.7, 0.7, 1.0 };
1548 /*    static const GLfloat amb1[]  = { 0.7, 0.0, 0.0, 1.0 }; */
1549       static const GLfloat dif0[]  = { 1.0, 1.0, 1.0, 1.0 };
1550       static const GLfloat dif1[]  = { 0.3, 0.1, 0.1, 1.0 };
1551
1552       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1553       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1554
1555       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb0);
1556 /*    glLightfv(GL_LIGHT1, GL_AMBIENT,  amb1); */
1557       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif0);
1558       glLightfv(GL_LIGHT1, GL_DIFFUSE,  dif1);
1559
1560       glEnable(GL_LIGHTING);
1561       glEnable(GL_LIGHT0);
1562 /*    glEnable(GL_LIGHT1); */
1563
1564       glEnable(GL_DEPTH_TEST);
1565       glEnable(GL_TEXTURE_2D);
1566       glEnable(GL_NORMALIZE);
1567       glEnable(GL_CULL_FACE);
1568     }
1569
1570   if (do_texture)
1571     {
1572       int i;
1573       for (i = 0; i < countof(lc->texids); i++)
1574         glGenTextures(1, &lc->texids[i]);
1575
1576       lc->texture = xpm_to_ximage (mi->dpy,
1577                                    mi->xgwa.visual,
1578                                    mi->xgwa.colormap,
1579                                    lament512);
1580
1581       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
1582       /* messes up -fps */
1583       /* glPixelStorei(GL_UNPACK_ROW_LENGTH, lc->texture->width); */
1584
1585       for (i = 0; i < countof(lc->texids); i++)
1586         {
1587           int height = lc->texture->width;      /* assume square */
1588           glBindTexture(GL_TEXTURE_2D, lc->texids[i]);
1589
1590           clear_gl_error();
1591           glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1592                        lc->texture->width, height, 0,
1593                        GL_RGBA,
1594                        /* GL_UNSIGNED_BYTE, */
1595                        GL_UNSIGNED_INT_8_8_8_8_REV,
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, NULL);
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 */