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