From http://www.jwz.org/xscreensaver/xscreensaver-5.31.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         GLfloat d = 0.21582;
917         int i;
918         const int lists[4] = { OBJ_LID_A, OBJ_LID_B, OBJ_LID_C, OBJ_LID_D };
919
920         lc->facing_p = facing_screen_p (mi);
921
922         if (lc->anim_z < 0.5)
923           glTranslatef (0, -30 * lc->anim_z, 0);    /* zoom */
924         else
925           glTranslatef (8 * (0.5 - (lc->anim_z - 0.5)), 0, 0);
926
927         glCallList (lc->dlists[OBJ_LID_BASE]);
928         mi->polygon_count += lc->polys[OBJ_LID_BASE];
929         for (i = 0; i < countof(lists); i++)
930           {
931             glPushMatrix();
932             glRotatef (90 * i, 0, 1, 0);
933             glTranslatef (-d, -0.5, d);
934             glRotatef (-45, 0, 1, 0);
935             glRotatef (-lc->anim_r, 1, 0, 0);
936             glRotatef (45, 0, 1, 0);
937             glTranslatef (d, 0.5, -d);
938             glRotatef (-90 * i, 0, 1, 0);
939             glCallList (lc->dlists[lists[i]]);
940             mi->polygon_count += lc->polys[lists[i]];
941             glPopMatrix();
942           }
943
944 # ifdef DEBUG_MODE
945         if (lc->facing_p)
946           {
947             glColor3f(1, 0, 0);
948             glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
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             glVertex3f (-0.49, 0.49,  0.49);
953             glEnd();
954             mi->polygon_count++;
955           }
956 # endif /* DEBUG_MODE */
957       }
958       break;
959
960     case LAMENT_TASER_OUT:
961     case LAMENT_TASER_SLIDE:
962     case LAMENT_TASER_SLIDE_IN:
963     case LAMENT_TASER_IN:
964
965       glTranslatef (0, -lc->anim_z/2, 0);
966       glCallList (lc->dlists[OBJ_TASER_BASE]);
967       mi->polygon_count += lc->polys[OBJ_TASER_BASE];
968
969       glTranslatef (0, lc->anim_z, 0);
970       glCallList (lc->dlists[OBJ_TASER_A]);
971       mi->polygon_count += lc->polys[OBJ_TASER_A];
972
973       glTranslatef (lc->anim_y, 0, 0);
974       glCallList (lc->dlists[OBJ_TASER_B]);
975       mi->polygon_count += lc->polys[OBJ_TASER_B];
976       break;
977
978     case LAMENT_PILLAR_OUT:
979     case LAMENT_PILLAR_SPIN:
980     case LAMENT_PILLAR_IN:
981
982       glCallList (lc->dlists[OBJ_PILLAR_BASE]);
983       mi->polygon_count += lc->polys[OBJ_PILLAR_BASE];
984
985       glPushMatrix();
986       if (lc->anim_z == 1 || lc->anim_z == 3)
987         {
988           glRotatef (lc->anim_r, 0, 0, 1);
989           glTranslatef (0, 0, lc->anim_y);
990         }
991       glCallList (lc->dlists[OBJ_PILLAR_A]);
992       mi->polygon_count += lc->polys[OBJ_PILLAR_A];
993       glPopMatrix();
994
995       glPushMatrix();
996       if (lc->anim_z == 2 || lc->anim_z == 3)
997         {
998           glRotatef (lc->anim_r, 0, 0, 1);
999           glTranslatef (0, 0, -lc->anim_y);
1000         }
1001       glCallList (lc->dlists[OBJ_PILLAR_B]);
1002       mi->polygon_count += lc->polys[OBJ_PILLAR_B];
1003       glPopMatrix();
1004       break;
1005
1006     case LAMENT_SPHERE_OUT:
1007     case LAMENT_SPHERE_IN:
1008       mi->polygon_count += lament_sphere (mi, lc->anim_y);
1009       break;
1010
1011     case LAMENT_LEVIATHAN_SPIN:
1012     case LAMENT_LEVIATHAN_UNSPIN:
1013     case LAMENT_LEVIATHAN_FADE:
1014     case LAMENT_LEVIATHAN_UNFADE:
1015     case LAMENT_LEVIATHAN_TWIST:
1016     case LAMENT_LEVIATHAN_UNTWIST:
1017       {
1018         /* These normals are hard to compute, so I pulled them from the
1019            model. */
1020         const GLfloat axes[6][4] =
1021           {{ OBJ_ISO_UNE,  0.633994, 0.442836,   0.633994 },
1022            { OBJ_ISO_USW,  0.442836,  0.633994, -0.633994 },
1023            { OBJ_ISO_DSE, -0.633994,  0.633994,  0.442836 },
1024            { OBJ_ISO_SWD, -0.633994, -0.442836, -0.633994 },
1025            { OBJ_ISO_DEN, -0.442836, -0.633994,  0.633994 },
1026            { OBJ_ISO_UNW,  0.633994, -0.633994, -0.442836 }};
1027         int i;
1028
1029         GLfloat s = (1 - lc->anim_z);
1030         GLfloat s2 = MAX (0, 360 - lc->anim_r) / 360.0;
1031         Bool blendp = 0;
1032
1033         switch (lc->type) {
1034         case LAMENT_LEVIATHAN_SPIN: break;
1035         case LAMENT_LEVIATHAN_UNSPIN: s2 = 1 - s2; break;
1036         default: s2 = 0; blendp = 1; break;
1037         }
1038
1039         if (wire) blendp = 0;
1040
1041         s  = (s * 0.6) + 0.4;
1042
1043         leviathan (mi, 1 - s2, 1, True);
1044         glCallList (lc->dlists[OBJ_ISO_BASE_A]);
1045         mi->polygon_count += lc->polys[OBJ_ISO_BASE_A];
1046
1047         glPushMatrix();
1048         glScalef (s2, s2, s2);
1049         glCallList (lc->dlists[OBJ_ISO_USE]);
1050         mi->polygon_count += lc->polys[OBJ_ISO_USE];
1051         glPopMatrix();
1052
1053         glPushMatrix();
1054         glRotatef (lc->anim_y, 1, -1, 1);
1055         glCallList (lc->dlists[OBJ_ISO_BASE_B]);
1056         mi->polygon_count += lc->polys[OBJ_ISO_BASE_B];
1057         leviathan (mi, 1 - s2, 1, False);
1058         glPopMatrix();
1059
1060         if (blendp)
1061           {
1062 # ifndef HAVE_JWZGLES /* no glBlendColor */
1063             glEnable (GL_BLEND);
1064             glBlendFunc (GL_CONSTANT_ALPHA, GL_SRC_ALPHA);
1065             glBlendColor (1, 1, 1, MAX(0, 1-(lc->anim_z * 3)));
1066 # endif
1067           }
1068
1069         for (i = 0; i < countof(axes); i++)
1070           {
1071             glPushMatrix();
1072             glRotatef (lc->anim_r, axes[i][1], axes[i][2], axes[i][3]);
1073             glScalef (s, s, s);
1074             glCallList (lc->dlists[(int) axes[i][0]]);
1075             mi->polygon_count += lc->polys[(int) axes[i][0]];
1076             glPopMatrix();
1077             if (i == 2)
1078               glRotatef (lc->anim_y, 1, -1, 1);
1079           }
1080
1081         if (blendp) glDisable (GL_BLEND);
1082
1083         glPushMatrix();
1084         glScalef (s2, s2, s2);
1085         glCallList (lc->dlists[OBJ_ISO_DWN]);
1086         mi->polygon_count += lc->polys[OBJ_ISO_DWN];
1087         glPopMatrix();
1088       }
1089       break;
1090
1091     case LAMENT_LEVIATHAN_COLLAPSE:
1092     case LAMENT_LEVIATHAN_EXPAND:
1093       {
1094         glPushMatrix();
1095         leviathan (mi, 1, lc->anim_y, True);
1096         glRotatef (180, 1, -1, 1);
1097         leviathan (mi, 1, lc->anim_y, False);
1098         glPopMatrix();
1099         folding_walls (mi, lc->anim_y, True);
1100         folding_walls (mi, lc->anim_y, False);
1101       }
1102       break;
1103
1104     default:
1105       abort();
1106       break;
1107     }
1108
1109   glPopMatrix();
1110 }
1111
1112
1113 /* Rather than just picking states randomly, pick an ordering randomly, do it,
1114    and then re-randomize.  That way one can be assured of seeing all states in
1115    a short time period, though not always in the same order (it's frustrating
1116    to see it pick the same state 5x in a row.)  Though, that can still happen,
1117    since states are in the list multiple times as a way of giving them
1118    probabilities.
1119  */
1120 static void
1121 shuffle_states (lament_configuration *lc)
1122 {
1123   int i;
1124   for (i = 0; i < lc->nstates; i++)
1125     {
1126       int a = random() % lc->nstates;
1127       lament_type swap = lc->states[a];
1128       lc->states[a] = lc->states[i];
1129       lc->states[i] = swap;
1130     }
1131 }
1132
1133
1134 static void
1135 animate (ModeInfo *mi)
1136 {
1137   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1138   int pause = 10;
1139   int pause2 = 120;
1140   GLfloat speed = (lc->ffwdp ? 20 : 1);
1141
1142   switch (lc->type)
1143     {
1144     case LAMENT_BOX:
1145       {
1146         lc->state++;
1147         if (lc->state >= lc->nstates)
1148           {
1149             shuffle_states (lc);
1150             lc->state = 0;
1151           }
1152         lc->type = lc->states[lc->state];
1153
1154         if (lc->type == LAMENT_BOX)
1155           lc->anim_pause = pause2;
1156
1157         lc->anim_r = 0.0;
1158         lc->anim_y = 0.0;
1159         lc->anim_z = 0.0;
1160       }
1161       break;
1162
1163       /* -------------------------------------------------------------- */
1164
1165     case LAMENT_STAR_OUT:
1166       lc->anim_z += 0.01 * speed;
1167       if (lc->anim_z >= 1.0)
1168         {
1169           lc->anim_z = 1.0;
1170           lc->type = LAMENT_STAR_ROT;
1171           lc->anim_pause = pause;
1172         }
1173       break;
1174
1175     case LAMENT_STAR_ROT:
1176       lc->anim_r += 1.0 * speed;
1177       if (lc->anim_r >= 45.0)
1178         {
1179           lc->anim_r = 45.0;
1180           lc->type = LAMENT_STAR_ROT_IN;
1181           lc->anim_pause = pause;
1182         }
1183       break;
1184
1185     case LAMENT_STAR_ROT_IN:
1186       lc->anim_z -= 0.01 * speed;
1187       if (lc->anim_z <= 0.0)
1188         {
1189           lc->anim_z = 0.0;
1190           lc->type = LAMENT_STAR_ROT_OUT;
1191           lc->anim_pause = pause2 * (1 + frand(2) + frand(2));
1192         }
1193       break;
1194
1195     case LAMENT_STAR_ROT_OUT:
1196       lc->anim_z += 0.01 * speed;
1197       if (lc->anim_z >= 1.0)
1198         {
1199           lc->anim_z = 1.0;
1200           lc->type = LAMENT_STAR_UNROT;
1201           lc->anim_pause = pause;
1202         }
1203       break;
1204       
1205     case LAMENT_STAR_UNROT:
1206       lc->anim_r -= 1.0 * speed;
1207       if (lc->anim_r <= 0.0)
1208         {
1209           lc->anim_r = 0.0;
1210           lc->type = LAMENT_STAR_IN;
1211           lc->anim_pause = pause;
1212         }
1213       break;
1214
1215     case LAMENT_STAR_IN:
1216       lc->anim_z -= 0.01 * speed;
1217       if (lc->anim_z <= 0.0)
1218         {
1219           lc->anim_z = 0.0;
1220           lc->type = LAMENT_BOX;
1221           lc->anim_pause = pause2;
1222         }
1223       break;
1224
1225       /* -------------------------------------------------------------- */
1226
1227     case LAMENT_TETRA_UNE:
1228     case LAMENT_TETRA_USW:
1229     case LAMENT_TETRA_DWN:
1230     case LAMENT_TETRA_DSE:
1231
1232       lc->anim_r += 1.0 * speed;
1233       if (lc->anim_r >= 360.0)
1234         {
1235           lc->anim_r = 0.0;
1236           lc->type = LAMENT_BOX;
1237           lc->anim_pause = pause2;
1238         }
1239       else if (lc->anim_r > 119.0 && lc->anim_r <= 120.0)
1240         {
1241           lc->anim_r = 120.0;
1242           lc->anim_pause = pause;
1243         }
1244       else if (lc->anim_r > 239.0 && lc->anim_r <= 240.0)
1245         {
1246           lc->anim_r = 240.0;
1247           lc->anim_pause = pause;
1248         }
1249       break;
1250
1251       /* -------------------------------------------------------------- */
1252
1253     case LAMENT_LID_OPEN:
1254       lc->anim_r += 1.0 * speed;
1255
1256       if (lc->anim_r >= 112.0)
1257         {
1258           lc->anim_r = 112.0;
1259           lc->anim_z = 0.0;
1260           lc->anim_pause = pause2;
1261           lc->type = (lc->facing_p ? LAMENT_LID_ZOOM : LAMENT_LID_CLOSE);
1262         }
1263       break;
1264
1265     case LAMENT_LID_CLOSE:
1266       lc->anim_r -= 1.0 * speed;
1267       if (lc->anim_r <= 0.0)
1268         {
1269           lc->anim_r = 0.0;
1270           lc->type = LAMENT_BOX;
1271           lc->anim_pause = pause2;
1272         }
1273       break;
1274
1275     case LAMENT_LID_ZOOM:
1276       lc->anim_z += 0.01 * speed;
1277       if (lc->anim_z > 1.0)
1278         {
1279           lc->anim_r = 0.0;
1280           lc->anim_z = 0.0;
1281           lc->type = LAMENT_BOX;
1282         }
1283       break;
1284
1285       /* -------------------------------------------------------------- */
1286
1287     case LAMENT_TASER_OUT:
1288       lc->anim_z += 0.005 * speed;
1289       if (lc->anim_z >= 0.5)
1290         {
1291           lc->anim_z = 0.5;
1292           lc->type = LAMENT_TASER_SLIDE;
1293           lc->anim_pause = pause * (1 + frand(5) + frand(5));
1294         }
1295       break;
1296
1297     case LAMENT_TASER_SLIDE:
1298       lc->anim_y += 0.005 * speed;
1299       if (lc->anim_y >= 0.255)
1300         {
1301           lc->anim_y = 0.255;
1302           lc->type = LAMENT_TASER_SLIDE_IN;
1303           lc->anim_pause = pause2 * (1 + frand(5) + frand(5));
1304         }
1305       break;
1306
1307     case LAMENT_TASER_SLIDE_IN:
1308       lc->anim_y -= 0.0025 * speed;
1309       if (lc->anim_y <= 0.0)
1310         {
1311           lc->anim_y = 0.0;
1312           lc->type = LAMENT_TASER_IN;
1313           lc->anim_pause = pause;
1314         }
1315       break;
1316
1317     case LAMENT_TASER_IN:
1318       lc->anim_z -= 0.0025 * speed;
1319       if (lc->anim_z <= 0.0)
1320         {
1321           lc->anim_z = 0.0;
1322           lc->type = LAMENT_BOX;
1323           lc->anim_pause = pause2;
1324         }
1325       break;
1326
1327       /* -------------------------------------------------------------- */
1328
1329     case LAMENT_PILLAR_OUT:
1330
1331       if (lc->anim_y == 0)  /* mostly in */
1332         lc->anim_y += 0.005 * ((random() % 5) ? -1 : 1) * speed;
1333       else if (lc->anim_y > 0)
1334         lc->anim_y += 0.005 * speed;
1335       else
1336         lc->anim_y -= 0.001 * speed;
1337
1338       if (lc->anim_z == 0)
1339         {
1340           int i = (random() % 7);   /* A, B or both */
1341           if      (i == 0) lc->anim_z = 3;
1342           else if (i < 5)  lc->anim_z = 2;
1343           else             lc->anim_z = 1;
1344
1345           /* We can do quarter turns, because it's radially symmetrical. */
1346           lc->anim_r =  90.0 * (1 + frand(6)) * RANDSIGN();
1347         }
1348       if (lc->anim_y > 0.4)
1349         {
1350           lc->anim_y = 0.4;
1351           lc->type = LAMENT_PILLAR_SPIN;
1352           lc->anim_pause = pause;
1353         }
1354       else if (lc->anim_y < -0.03)
1355         {
1356           lc->anim_y = -0.03;
1357           lc->type = LAMENT_PILLAR_SPIN;
1358           lc->anim_pause = pause;
1359         }
1360       break;
1361
1362     case LAMENT_PILLAR_SPIN:
1363       {
1364         Bool negp = (lc->anim_r < 0);
1365         lc->anim_r += (negp ? 1 : -1) * speed;
1366         if (negp ? lc->anim_r > 0 : lc->anim_r < 0)
1367           {
1368             lc->anim_r = 0;
1369             lc->type = LAMENT_PILLAR_IN;
1370           }
1371       }
1372       break;
1373
1374     case LAMENT_PILLAR_IN:
1375       {
1376         Bool negp = (lc->anim_y < 0);
1377         lc->anim_y += (negp ? 1 : -1) * 0.005 * speed;
1378         if (negp ? lc->anim_y > 0 : lc->anim_y < 0)
1379           {
1380             lc->anim_y = 0;
1381             lc->anim_z = 0;
1382             lc->type = LAMENT_BOX;
1383             lc->anim_pause = pause;
1384           }
1385       }
1386       break;
1387
1388       /* -------------------------------------------------------------- */
1389
1390     case LAMENT_SPHERE_OUT:
1391       {
1392         lc->anim_y += 0.01 * speed;
1393         if (lc->anim_y >= 1)
1394           {
1395             lc->anim_y = 1;
1396             lc->type = LAMENT_SPHERE_IN;
1397             lc->anim_pause = pause2 * (1 + frand(1) + frand(1));
1398           }
1399       }
1400       break;
1401
1402     case LAMENT_SPHERE_IN:
1403       {
1404         lc->anim_y -= 0.01 * speed;
1405         if (lc->anim_y <= 0)
1406           {
1407             lc->anim_y = 0;
1408             lc->type = LAMENT_BOX;
1409             lc->anim_pause = pause;
1410           }
1411       }
1412       break;
1413
1414       /* -------------------------------------------------------------- */
1415
1416     case LAMENT_LEVIATHAN_SPIN:
1417       lc->anim_r += 3.5 * speed;
1418       if (lc->anim_r >= 360 * 3)
1419         {
1420           lc->anim_r = 0;
1421           lc->type = LAMENT_LEVIATHAN_FADE;
1422           lc->anim_pause = 0;
1423         }
1424       break;
1425
1426     case LAMENT_LEVIATHAN_FADE:
1427       lc->anim_z += 0.01 * speed;
1428       if (lc->anim_z >= 1)
1429         {
1430           lc->anim_z = 1;
1431           lc->type = LAMENT_LEVIATHAN_TWIST;
1432           lc->anim_pause = 0;
1433         }
1434       break;
1435
1436     case LAMENT_LEVIATHAN_TWIST:
1437       lc->anim_y += 2 * speed;
1438       lc->anim_z = 1;
1439       if (lc->anim_y >= 180)
1440         {
1441           lc->anim_y = 0;
1442           lc->type = LAMENT_LEVIATHAN_COLLAPSE;
1443           lc->anim_pause = 0;
1444         }
1445       break;
1446
1447     case LAMENT_LEVIATHAN_COLLAPSE:
1448       lc->anim_y += 0.01 * speed;
1449       if (lc->anim_y >= 1)
1450         {
1451           lc->anim_y = 1.0;
1452           lc->type = LAMENT_LEVIATHAN_EXPAND;
1453           lc->anim_pause = pause2 * 4;
1454         }
1455       break;
1456
1457     case LAMENT_LEVIATHAN_EXPAND:
1458       lc->anim_y -= 0.005 * speed;
1459       if (lc->anim_y <= 0)
1460         {
1461           lc->anim_y = 180;
1462           lc->type = LAMENT_LEVIATHAN_UNTWIST;
1463         }
1464       break;
1465
1466     case LAMENT_LEVIATHAN_UNTWIST:
1467       lc->anim_y -= 2 * speed;
1468       lc->anim_z = 1;
1469       if (lc->anim_y <= 0)
1470         {
1471           lc->anim_y = 0;
1472           lc->type = LAMENT_LEVIATHAN_UNFADE;
1473           lc->anim_pause = 0;
1474         }
1475       break;
1476
1477     case LAMENT_LEVIATHAN_UNFADE:
1478       lc->anim_z -= 0.1 * speed;
1479       if (lc->anim_z <= 0)
1480         {
1481           lc->anim_z = 0;
1482           lc->type = LAMENT_LEVIATHAN_UNSPIN;
1483           lc->anim_pause = 0;
1484         }
1485       break;
1486
1487     case LAMENT_LEVIATHAN_UNSPIN:
1488       lc->anim_r += 3.5 * speed;
1489       if (lc->anim_r >= 360 * 2)
1490         {
1491           lc->anim_r = 0;
1492           lc->type = LAMENT_BOX;
1493           lc->anim_pause = pause2;
1494         }
1495       break;
1496
1497     default:
1498       abort();
1499       break;
1500     }
1501
1502 # ifdef DEBUG_MODE
1503
1504   lc->anim_pause = 0;
1505
1506   if (lc->type == LAMENT_BOX)
1507     lc->type = DEBUG_MODE;
1508
1509   if (lc->ffwdp)
1510     {
1511       lc->ffwdp = 0;
1512       while (lc->type != DEBUG_MODE)
1513         animate (mi);
1514     }
1515
1516 # else /* !DEBUG_MODE */
1517
1518   if (lc->ffwdp && lc->type == LAMENT_BOX)
1519     {
1520       lc->ffwdp = 0;
1521       while (lc->type == LAMENT_BOX)
1522         animate (mi);
1523       lc->anim_pause = 0;
1524     }
1525
1526 # endif /* !DEBUG_MODE */
1527 }
1528
1529
1530 static void
1531 gl_init (ModeInfo *mi)
1532 {
1533   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1534   Bool wire = MI_IS_WIREFRAME(mi);
1535   int i;
1536
1537   if (wire)
1538     do_texture = False;
1539
1540   if (!wire)
1541     {
1542       static const GLfloat pos0[]  = { -4.0,  2.0, 5.0, 1.0 };
1543       static const GLfloat pos1[]  = {  6.0, -1.0, 3.0, 1.0 };
1544
1545       static const GLfloat amb0[]  = { 0.7, 0.7, 0.7, 1.0 };
1546 /*    static const GLfloat amb1[]  = { 0.7, 0.0, 0.0, 1.0 }; */
1547       static const GLfloat dif0[]  = { 1.0, 1.0, 1.0, 1.0 };
1548       static const GLfloat dif1[]  = { 0.3, 0.1, 0.1, 1.0 };
1549
1550       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1551       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1552
1553       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb0);
1554 /*    glLightfv(GL_LIGHT1, GL_AMBIENT,  amb1); */
1555       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif0);
1556       glLightfv(GL_LIGHT1, GL_DIFFUSE,  dif1);
1557
1558       glEnable(GL_LIGHTING);
1559       glEnable(GL_LIGHT0);
1560 /*    glEnable(GL_LIGHT1); */
1561
1562       glEnable(GL_DEPTH_TEST);
1563       glEnable(GL_TEXTURE_2D);
1564       glEnable(GL_NORMALIZE);
1565       glEnable(GL_CULL_FACE);
1566     }
1567
1568   if (do_texture)
1569     {
1570       int i;
1571       for (i = 0; i < countof(lc->texids); i++)
1572         glGenTextures(1, &lc->texids[i]);
1573
1574       lc->texture = xpm_to_ximage (mi->dpy,
1575                                    mi->xgwa.visual,
1576                                    mi->xgwa.colormap,
1577                                    lament512);
1578
1579       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
1580       /* messes up -fps */
1581       /* glPixelStorei(GL_UNPACK_ROW_LENGTH, lc->texture->width); */
1582
1583       for (i = 0; i < countof(lc->texids); i++)
1584         {
1585           int height = lc->texture->width;      /* assume square */
1586           glBindTexture(GL_TEXTURE_2D, lc->texids[i]);
1587
1588           clear_gl_error();
1589           glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1590                        lc->texture->width, height, 0,
1591                        GL_RGBA,
1592                        /* GL_UNSIGNED_BYTE, */
1593                        GL_UNSIGNED_INT_8_8_8_8_REV,
1594                        (lc->texture->data +
1595                         (lc->texture->bytes_per_line * height * i)));
1596           check_gl_error("texture");
1597
1598           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1599           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1600           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1601           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1602           check_gl_error("texture");
1603
1604           /* This makes scaled pixmaps tolerable to look at. */
1605 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
1606 #   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
1607 # endif
1608 # ifdef GL_TEXTURE_LOD_BIAS
1609           glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
1610 # endif
1611           clear_gl_error();  /* invalid enum on iPad 3 */
1612         }
1613     }
1614
1615   for (i = 0; i < countof(all_objs); i++)
1616     {
1617       GLfloat s = 1/3.0;  /* box is 3" square */
1618       const struct gllist *L = *all_objs[i];
1619       const GLfloat *f = (const GLfloat *) L->data;
1620       int j;
1621
1622       lc->dlists[i] = glGenLists(1);
1623       lc->polys[i] = L->points / 3;
1624       glNewList(lc->dlists[i], GL_COMPILE);
1625       if (L->primitive != GL_TRIANGLES) abort();
1626       if (L->format != GL_N3F_V3F) abort();
1627
1628       glPushMatrix();
1629       glTranslatef (-0.5, -0.5, -0.5);
1630       glScalef (s, s, s);
1631       
1632       for (j = 0; j < L->points; j += 3)
1633         {
1634           int face, outerp;
1635           Bool blackp = (i == OBJ_ISO_BASE_A || i == OBJ_ISO_BASE_B);
1636           which_face (mi, f, &face, &outerp); /* from norm of first vert */
1637
1638           set_colors (outerp ? exterior_color :
1639                       blackp ? black_color : interior_color);
1640           glBindTexture (GL_TEXTURE_2D, 
1641                          (outerp ? lc->texids[face-1] :
1642                           blackp ? 0 : lc->texids[6]));
1643
1644           glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
1645           if (face) texturize_vert (mi, face, f+3);
1646           glNormal3fv (f); f += 3; glVertex3fv (f); f += 3;
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           glEnd();
1652         }
1653       glPopMatrix();
1654
1655       glEndList();
1656     }
1657 }
1658
1659
1660 ENTRYPOINT Bool
1661 lament_handle_event (ModeInfo *mi, XEvent *event)
1662 {
1663   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1664
1665   if (gltrackball_event_handler (event, lc->trackball,
1666                                  MI_WIDTH (mi), MI_HEIGHT (mi),
1667                                  &lc->button_down_p))
1668     return True;
1669   else if (event->xany.type == KeyPress)
1670     {
1671       KeySym keysym;
1672       char c = 0;
1673       XLookupString (&event->xkey, &c, 1, &keysym, 0);
1674       if (c == ' ' || c == '\t')
1675         {
1676           lc->ffwdp = True;
1677           return True;
1678         }
1679     }
1680
1681   return False;
1682 }
1683
1684
1685 ENTRYPOINT void
1686 reshape_lament (ModeInfo *mi, int width, int height)
1687 {
1688   GLfloat h = (GLfloat) height / (GLfloat) width;
1689   glViewport(0, 0, (GLint) width, (GLint) height);
1690
1691   glMatrixMode(GL_PROJECTION);
1692   glLoadIdentity();
1693   glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
1694   glMatrixMode(GL_MODELVIEW);
1695   glLoadIdentity();
1696   glTranslatef(0.0, 0.0, -40.0);
1697   glClear(GL_COLOR_BUFFER_BIT);
1698 }
1699
1700
1701 ENTRYPOINT void
1702 init_lament (ModeInfo *mi)
1703 {
1704   lament_configuration *lc;
1705   int i;
1706   if (!lcs)
1707     {
1708       lcs = (lament_configuration *)
1709         calloc(MI_NUM_SCREENS(mi), sizeof (lament_configuration));
1710       if (!lcs)
1711         {
1712           fprintf(stderr, "%s: out of memory\n", progname);
1713           exit(1);
1714         }
1715     }
1716
1717   lc = &lcs[MI_SCREEN(mi)];
1718
1719   {
1720     double rot_speed = 0.5;
1721     lc->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
1722     lc->trackball = gltrackball_init (True);
1723   }
1724
1725   lc->type = LAMENT_BOX;
1726   lc->anim_pause = 300 + (random() % 100);
1727
1728   if ((lc->glx_context = init_GL(mi)) != NULL)
1729     {
1730       reshape_lament(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1731       gl_init(mi);
1732     }
1733
1734   lc->states = (lament_type *) calloc (200, sizeof (*lc->states));
1735   lc->nstates = 0;
1736
1737 # define PUSH(N,WHICH) \
1738     for (i = 0; i < N; i++) lc->states[lc->nstates++] = WHICH
1739
1740   PUSH (4, LAMENT_TETRA_UNE);           /* most common */
1741   PUSH (4, LAMENT_TETRA_USW);
1742   PUSH (4, LAMENT_TETRA_DWN);
1743   PUSH (4, LAMENT_TETRA_DSE);
1744
1745   PUSH (8, LAMENT_STAR_OUT);            /* pretty common */
1746   PUSH (8, LAMENT_TASER_OUT);
1747   PUSH (8, LAMENT_PILLAR_OUT);
1748
1749   PUSH (4, LAMENT_LID_OPEN);            /* rare */
1750   PUSH (2, LAMENT_SPHERE_OUT);          /* rare */
1751   PUSH (1, LAMENT_LEVIATHAN_SPIN);      /* very rare */
1752
1753   PUSH (35, LAMENT_BOX);                /* rest state */
1754 # undef PUSH
1755
1756   shuffle_states (lc);
1757
1758 # ifdef DEBUG_MODE
1759   lc->type = DEBUG_MODE;
1760   lc->anim_pause = 0;
1761 # endif
1762
1763 }
1764
1765
1766 ENTRYPOINT void
1767 draw_lament (ModeInfo *mi)
1768 {
1769   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
1770   Display *dpy = MI_DISPLAY(mi);
1771   Window window = MI_WINDOW(mi);
1772
1773   if (!lc->glx_context)
1774     return;
1775
1776   glDrawBuffer(GL_BACK);
1777
1778   glXMakeCurrent(dpy, window, *(lc->glx_context));
1779   draw(mi);
1780   if (mi->fps_p) do_fps (mi);
1781
1782   glFinish();
1783   glXSwapBuffers(dpy, window);
1784
1785   if (!lc->ffwdp && lc->anim_pause)
1786     lc->anim_pause--;
1787   else
1788     animate(mi);
1789 }
1790
1791 XSCREENSAVER_MODULE ("Lament", lament)
1792
1793 #endif /* USE_GL */