From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / lament.c
index 8a2360958900f81b257f6518e750c5bafd28f3ac..f29b4322daa6ca85c7a58a54cd663f329cdbc21a 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1998 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1998-2014 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 
    TODO:
 
-     *  The "gold" color isn't quite right; it looks more like "yellow" than
-        "gold" to me.
-
-     *  For some reason, the interior surfaces are shinier than the exterior
-        surfaces.  I don't understand why, but this should be remedied.
-
-     *  Perhaps use a slightly-bumpy or oily texture for the interior surfaces?
-
-     *  Some of the edges don't line up perfectly (since the images are not
-        perfectly symetrical.)  Something should be done about this; either
-        making the edges overlap slightly (instead of leaving gaps) or fixing
-        the images so that the edges may be symmetrical.
-
-     *  I want the gold leaf to seem to be raised up from the surface, but I
-        think this isn't possible with OpenGL.  Supposedly, OpenGL only 
-        supports Gouraud shading (interpolating edge normals from face normals,
-        and shading smoothly) but bump-maps only work with Phong shading
-        (computing a normal for each rendered pixel.)
-
-     *  As far as I can tell, OpenGL doesn't do shadows.  As a result, the
-        forward-facing interior walls are drawn bright, not dark.  If it was
-        casting shadows properly, it wouldn't matter so much that the edges
-        don't quite line up, because the lines would be black, and thus not
-        visible.  But the edges don't match up, and so the bright interior
-        faces show through, and that sucks.
-
-       But apparently there are tricky ways around this:
-       http://reality.sgi.com/opengl/tips/rts/
-       I think these techniques require GLUT, however, which isn't 
-       (currently) required by any other xscreensaver hacks.
+     *  The gold leaf should appear to be raised up from the surface, but
+        I think this isn't possible with OpenGL.  No bump maps.
 
      *  There should be strange lighting effects playing across the surface:
-        electric sparks, or little glittery blobs of light.  
-        http://reality.sgi.com/opengl/tips/lensflare/ might provide guidance.
-
-     *  Need to add some more modes, to effect the transition from the cube
-        shapes to the "spike" or "leviathan" shapes.  I have extensive notes
-        on how these transformations occur, but unfortunately, due to camera
-        trickery, the transitions require dematerializations which do not
-        preserve object volume.  But I suppose that's allowed, in
-        non-Euclidian or hyperdimensional spaces (since the extra mass could
-        simply be rotated along the axis to which one cannot point.)
-
-        The other hard thing about this is that the "leviathan" shapes contain
-        a much larger number of facets, and I modelled this whole thing by 
-        hand, since I don't have any 3d-object-editing tools that I know how
-        to use (or that look like they would take any less than several months
-        to become even marginally proficient with...)
-
-     *  Perhaps there should be a table top, on which it casts a shadow?
-        And then multiple light sources (for multiple shadows)?
+        electric sparks, or little glittery blobs of light.  Maybe like
+        http://www.opengl.org/archives/resources/features/KilgardTechniques/
+        LensFlare/
+
+     *  Chains.
 
      *  Needs music.  ("Hellraiser Themes" by Coil: TORSO CD161; also
         duplicated on the "Unnatural History 2" compilation, WORLN M04699.)
-
-     *  I'm not totally happy with the spinning motion; I like the
-        acceleration and deceleration, but it often feels like it's going too
-        fast, or not naturally enough, or something.
-
-     *  However, the motion is better than that used by gears, superquadrics,
-        etc.; so maybe I should make them all share the same motion code.
  */
 
-#include <X11/Intrinsic.h>
-
-#define PROGCLASS      "Lament"
-#define HACK_INIT      init_lament
-#define HACK_DRAW      draw_lament
-#define lament_opts    xlockmore_opts
-#define DEFAULTS       "*delay:        10000   \n"     \
+#define DEFAULTS       "*delay:        20000   \n"     \
+                       "*showFPS:      False   \n"     \
                        "*wireframe:    False   \n"     \
-                       "*texture:      True    \n"
+                       "*suppressRotationAnimation: True\n" \
+
+# define refresh_lament 0
+# define release_lament 0
 #include "xlockmore.h"
 
 #ifdef USE_GL /* whole file */
 
+#include "gllist.h"
+
+/* #define DEBUG_MODE LAMENT_LEVIATHAN_COLLAPSE */
+
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
+#undef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#undef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+extern const struct gllist
+  *lament_model_box,
+  *lament_model_iso_base_a,
+  *lament_model_iso_base_b,
+  *lament_model_iso_den,
+  *lament_model_iso_dse,
+  *lament_model_iso_dwn,
+  *lament_model_iso_swd,
+  *lament_model_iso_une,
+  *lament_model_iso_unw,
+  *lament_model_iso_use,
+  *lament_model_iso_usw,
+  *lament_model_leviathan,
+  *lament_model_lid_a,
+  *lament_model_lid_b,
+  *lament_model_lid_base,
+  *lament_model_lid_c,
+  *lament_model_lid_d,
+  *lament_model_pillar_a,
+  *lament_model_pillar_b,
+  *lament_model_pillar_base,
+  *lament_model_star_d,
+  *lament_model_star_u,
+  *lament_model_taser_a,
+  *lament_model_taser_b,
+  *lament_model_taser_base,
+  *lament_model_tetra_base,
+  *lament_model_tetra_dse,
+  *lament_model_tetra_dwn,
+  *lament_model_tetra_une,
+  *lament_model_tetra_usw;
+
+static const struct gllist * const *all_objs[] = {
+  &lament_model_box,
+  &lament_model_iso_base_a,
+  &lament_model_iso_base_b,
+  &lament_model_iso_den,
+  &lament_model_iso_dse,
+  &lament_model_iso_dwn,
+  &lament_model_iso_swd,
+  &lament_model_iso_une,
+  &lament_model_iso_unw,
+  &lament_model_iso_use,
+  &lament_model_iso_usw,
+  &lament_model_leviathan,
+  &lament_model_lid_a,
+  &lament_model_lid_b,
+  &lament_model_lid_base,
+  &lament_model_lid_c,
+  &lament_model_lid_d,
+  &lament_model_pillar_a,
+  &lament_model_pillar_b,
+  &lament_model_pillar_base,
+  &lament_model_star_d,
+  &lament_model_star_u,
+  &lament_model_taser_a,
+  &lament_model_taser_b,
+  &lament_model_taser_base,
+  &lament_model_tetra_base,
+  &lament_model_tetra_dse,
+  &lament_model_tetra_dwn,
+  &lament_model_tetra_une,
+  &lament_model_tetra_usw
+};
+
+typedef enum { /* must be in the same order as in `all_objs'. */
+  OBJ_BOX = 0,
+  OBJ_ISO_BASE_A,
+  OBJ_ISO_BASE_B,
+  OBJ_ISO_DEN,
+  OBJ_ISO_DSE,
+  OBJ_ISO_DWN,
+  OBJ_ISO_SWD,
+  OBJ_ISO_UNE,
+  OBJ_ISO_UNW,
+  OBJ_ISO_USE,
+  OBJ_ISO_USW,
+  OBJ_LEVIATHAN,
+  OBJ_LID_A,
+  OBJ_LID_B,
+  OBJ_LID_BASE,
+  OBJ_LID_C,
+  OBJ_LID_D,
+  OBJ_PILLAR_A,
+  OBJ_PILLAR_B,
+  OBJ_PILLAR_BASE,
+  OBJ_STAR_D,
+  OBJ_STAR_U,
+  OBJ_TASER_A,
+  OBJ_TASER_B,
+  OBJ_TASER_BASE,
+  OBJ_TETRA_BASE,
+  OBJ_TETRA_DSE,
+  OBJ_TETRA_DWN,
+  OBJ_TETRA_UNE,
+  OBJ_TETRA_USW
+} lament_obj_index;
+
 
 #define DEF_TEXTURE "True"
 
 static int do_texture;
+
 static XrmOptionDescRec opts[] = {
-  {"-texture", ".lament.texture", XrmoptionNoArg, (caddr_t) "true" },
-  {"+texture", ".lament.texture", XrmoptionNoArg, (caddr_t) "false" },
+  {"-texture", ".lament.texture", XrmoptionNoArg, "true" },
+  {"+texture", ".lament.texture", XrmoptionNoArg, "false" },
 };
 
 static argtype vars[] = {
-  {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
+  {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
 };
 
-ModeSpecOpt lament_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt lament_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
 #include "xpm-ximage.h"
-#include "../images/lament.xpm"
+#include "rotator.h"
+#include "gltrackball.h"
+#include "normals.h"
+
+#ifdef __GNUC__
+ __extension__ /* don't warn about "string length is greater than the length
+                  ISO C89 compilers are required to support" when including
+                  the following XPM file... */
+#endif
+#include "../images/lament512.xpm"
 
-#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
 #define RANDSIGN() ((random() & 1) ? 1 : -1)
 
 typedef enum {
@@ -134,1476 +199,962 @@ typedef enum {
   LAMENT_TASER_OUT,
   LAMENT_TASER_SLIDE,
   LAMENT_TASER_SLIDE_IN,
-  LAMENT_TASER_IN
+  LAMENT_TASER_IN,
+
+  LAMENT_PILLAR_OUT,
+  LAMENT_PILLAR_SPIN,
+  LAMENT_PILLAR_IN,
+
+  LAMENT_SPHERE_OUT,
+  LAMENT_SPHERE_IN,
+
+  LAMENT_LEVIATHAN_SPIN,
+  LAMENT_LEVIATHAN_FADE,
+  LAMENT_LEVIATHAN_TWIST,
+  LAMENT_LEVIATHAN_COLLAPSE,
+  LAMENT_LEVIATHAN_EXPAND,
+  LAMENT_LEVIATHAN_UNTWIST,
+  LAMENT_LEVIATHAN_UNFADE,
+  LAMENT_LEVIATHAN_UNSPIN,
 
 } lament_type;
 
-static GLfloat exterior_color[] = { 0.70, 0.60, 0.00, 1.00 };
-static GLfloat interior_color[] = { 0.25, 0.25, 0.20, 1.00 };
+static const GLfloat exterior_color[] =
+ { 0.33, 0.22, 0.03, 1.00,  /* ambient    */
+   0.78, 0.57, 0.11, 1.00,  /* specular   */
+   0.99, 0.91, 0.81, 1.00,  /* diffuse    */
+   27.80                    /* shininess  */
+ };
+static const GLfloat interior_color[] =
+ { 0.20, 0.20, 0.15, 1.00,  /* ambient    */
+   0.40, 0.40, 0.32, 1.00,  /* specular   */
+   0.99, 0.99, 0.81, 1.00,  /* diffuse    */
+   50.80                    /* shininess  */
+ };
+static const GLfloat leviathan_color[] =
+ { 0.30, 0.30, 0.30, 1.00,  /* ambient    */
+   0.85, 0.85, 0.95, 1.00,  /* specular   */
+   0.99, 0.99, 0.99, 1.00,  /* diffuse    */
+   50.80                    /* shininess  */
+ };
+static const GLfloat black_color[] =
+ { 0.05, 0.05, 0.05, 1.00,  /* ambient    */
+   0.05, 0.05, 0.05, 1.00,  /* specular   */
+   0.05, 0.05, 0.05, 1.00,  /* diffuse    */
+   80.00                    /* shininess  */
+ };
 
 
 typedef struct {
   GLXContext *glx_context;
+  rotator *rot;
+  double rotx, roty, rotz;
+  trackball_state *trackball;
+  Bool button_down_p;
+  Bool ffwdp;
 
-  GLuint box;                     /* display list IDs */
-  GLuint star1, star2;
-  GLuint tetra_une, tetra_usw, tetra_dwn, tetra_dse, tetra_mid;
-  GLuint lid_0, lid_1, lid_2, lid_3, lid_4;
-  GLuint taser_base, taser_lifter, taser_slider;
+  GLuint dlists[countof(all_objs)];
+  GLuint polys[countof(all_objs)];
 
-  GLfloat rotx, roty, rotz;       /* current object rotation */
-  GLfloat dx, dy, dz;             /* current rotational velocity */
-  GLfloat ddx, ddy, ddz;          /* current rotational acceleration */
-  GLfloat d_max;                  /* max velocity */
   XImage *texture;                /* image bits */
-  GLuint texids[6];               /* texture map IDs */
+  GLuint texids[8];               /* texture map IDs */
   lament_type type;               /* which mode of the object is current */
 
   int anim_pause;                 /* countdown before animating again */
   GLfloat anim_r, anim_y, anim_z;  /* relative position during anims */
+  Bool facing_p;
+
+  int state, nstates;
+  lament_type *states;
 
 } lament_configuration;
 
 static lament_configuration *lcs = NULL;
 
-#define FACE_N 3
-#define FACE_S 2
-#define FACE_E 0
-#define FACE_W 4
-#define FACE_U 5
-#define FACE_D 1
 
-static void
-parse_image_data(ModeInfo *mi)
+static Bool
+facing_screen_p (ModeInfo *mi)
 {
-  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
-  lc->texture = xpm_to_ximage (mi->dpy,
-                              mi->xgwa.visual,
-                              mi->xgwa.colormap,
-                              lament_faces);
-}
-
-
-\f
-/* Computing normal vectors (thanks to Nat Friedman <ndf@mit.edu>)
- */
+  Bool facing_p;
+  GLdouble m[16], p[16], x, y, z;
+  GLint v[4];
+  glGetDoublev (GL_MODELVIEW_MATRIX, m);
+  glGetDoublev (GL_PROJECTION_MATRIX, p);
+  glGetIntegerv (GL_VIEWPORT, v);
+       
+  /* See if a coordinate 5 units in front of the door is near the
+     center of the screen. */
+  gluProject (0, -5, 0, m, p, v, &x, &y, &z);
+  x = (x / MI_WIDTH(mi))  - 0.5;
+  y = (y / MI_HEIGHT(mi)) - 0.5;
+
+  facing_p = (z < 0.9 &&
+              x > -0.15 && x < 0.15 &&
+              y > -0.15 && y < 0.15);
+
+# ifdef DEBUG_MODE
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glDisable (GL_LIGHTING);
+  glColor3f (1, (facing_p ? 1 : 0), 0);
+  glBegin (GL_LINES);
+  glVertex3f (0, 0, 0);
+  glVertex3f (0, -5, 0);
+  glEnd();
+  if (!MI_IS_WIREFRAME(mi)) glEnable (GL_LIGHTING);
+# endif /* DEBUG_MODE */
 
-typedef struct vector {
-  GLfloat x, y, z;
-} vector;
+  return facing_p;
+}
 
-typedef struct plane {
-  vector p1, p2, p3;
-} plane;
 
 static void
-vector_set(vector *v, GLfloat x, GLfloat y, GLfloat z)
+scale_for_window (ModeInfo *mi)
 {
-  v->x = x;
-  v->y = y;
-  v->z = z;
-}
+  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
 
-static void
-vector_cross(vector v1, vector v2, vector *v3)
-{
-  v3->x = (v1.y * v2.z) - (v1.z * v2.y);
-  v3->y = (v1.z * v2.x) - (v1.x * v2.z);
-  v3->z = (v1.x * v2.y) - (v1.y * v2.x);
-}
+  GLfloat target_size = 1.4 * (lc->texture ? lc->texture->width : 512);
+  GLfloat size = MI_WIDTH(mi) < MI_HEIGHT(mi) ? MI_WIDTH(mi) : MI_HEIGHT(mi);
+  GLfloat scale;
 
-static void
-vector_subtract(vector v1, vector v2, vector *res)
-{
-  res->x = v1.x - v2.x;
-  res->y = v1.y - v2.y;
-  res->z = v1.z - v2.z;
-}
+  /* Make it take up roughly the full width of the window. */
+  scale = 20;
 
-static void
-plane_normal(plane p, vector *n)
-{
-  vector v1, v2;
-  vector_subtract(p.p1, p.p2, &v1);
-  vector_subtract(p.p1, p.p3, &v2);
-  vector_cross(v2, v1, n);
-}
+  /* But if the window is wider than tall, make it only take up the
+     height of the window instead.
+   */
+  if (MI_WIDTH(mi) > MI_HEIGHT(mi))
+    scale /= MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
 
-static void
-do_normal(GLfloat x1, GLfloat y1, GLfloat z1,
-         GLfloat x2, GLfloat y2, GLfloat z2,
-         GLfloat x3, GLfloat y3, GLfloat z3)
-{
-  plane plane;
-  vector n;
-  vector_set(&plane.p1, x1, y1, z1);
-  vector_set(&plane.p2, x2, y2, z2);
-  vector_set(&plane.p3, x3, y3, z3);
-  plane_normal(plane, &n);
-  n.x = -n.x; n.y = -n.y; n.z = -n.z;
-
-  glNormal3f(n.x, n.y, n.z);
-
-#ifdef DEBUG
-  /* Draw a line in the direction of this face's normal. */
-  {
-    GLfloat ax = n.x > 0 ? n.x : -n.x;
-    GLfloat ay = n.y > 0 ? n.y : -n.y;
-    GLfloat az = n.z > 0 ? n.z : -n.z;
-    GLfloat mx = (x1 + x2 + x3) / 3;
-    GLfloat my = (y1 + y2 + y3) / 3;
-    GLfloat mz = (z1 + z2 + z3) / 3;
-    GLfloat xx, yy, zz;
-
-    GLfloat max = ax > ay ? ax : ay;
-    if (az > max) max = az;
-    max *= 2;
-    xx = n.x / max;
-    yy = n.y / max;
-    zz = n.z / max;
-
-    glBegin(GL_LINE_LOOP);
-    glVertex3f(mx, my, mz);
-    glVertex3f(mx+xx, my+yy, mz+zz);
-    glEnd();
-  }
-#endif /* DEBUG */
-}
+  /* Constrain it to roughly life-sized on the screen, not huge.
+   */
+# ifdef HAVE_MOBILE
+  if (size > 768)  /* iPad retina / iPhone 6 */
+    target_size *= 1.5;
+  else
+# endif
+    {
+      GLfloat max = 500;  /* 3" on my screen... */
+      if (target_size > max)
+        target_size = max;
+    }
 
+  /* But if that would make the image larger than target_size, scale it
+     back down again.  The image-map bits we have are 512x512, so if the
+     image is magnified a lot, it looks pretty blocky.  It's better to
+     have a 512x512 animation on a 1920x1080 screen that looks good
+     than a 1024x1024 animation that looks really pixelated.
+   */
+  if (size > target_size)
+    scale *= target_size / size;
+
+  glScalef (scale, scale, scale);
+}
 
-\f
-/* Shorthand utilities for making faces, with proper normals.
- */
 
 static void
-face3(GLint texture, GLfloat *color, Bool wire,
-      GLfloat s1, GLfloat t1, GLfloat x1, GLfloat y1, GLfloat z1,
-      GLfloat s2, GLfloat t2, GLfloat x2, GLfloat y2, GLfloat z2,
-      GLfloat s3, GLfloat t3, GLfloat x3, GLfloat y3, GLfloat z3)
+set_colors (const GLfloat *color)
 {
-#ifdef HAVE_GLBINDTEXTURE
-  glBindTexture(GL_TEXTURE_2D, texture);
-#endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
-  do_normal(x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
-  glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLES);
-  glTexCoord2f(s1, t1); glVertex3f(x1, y1, z1);
-  glTexCoord2f(s2, t2); glVertex3f(x2, y2, z2);
-  glTexCoord2f(s3, t3); glVertex3f(x3, y3, z3);
-  glEnd();
+  glMaterialfv(GL_FRONT, GL_AMBIENT,   color + 0);
+  glMaterialfv(GL_FRONT, GL_DIFFUSE,   color + 4);
+  glMaterialfv(GL_FRONT, GL_SPECULAR,  color + 8);
+  glMaterialfv(GL_FRONT, GL_SHININESS, color + 12);
 }
 
 static void
-face4(GLint texture, GLfloat *color, Bool wire,
-      GLfloat s1, GLfloat t1, GLfloat x1, GLfloat y1, GLfloat z1,
-      GLfloat s2, GLfloat t2, GLfloat x2, GLfloat y2, GLfloat z2,
-      GLfloat s3, GLfloat t3, GLfloat x3, GLfloat y3, GLfloat z3,
-      GLfloat s4, GLfloat t4, GLfloat x4, GLfloat y4, GLfloat z4)
+set_colors_alpha (const GLfloat *color, GLfloat a)
 {
-#ifdef HAVE_GLBINDTEXTURE
-  glBindTexture(GL_TEXTURE_2D, texture);
-#endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
-  do_normal(x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
-  glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
-  glTexCoord2f(s1, t1); glVertex3f(x1, y1, z1);
-  glTexCoord2f(s2, t2); glVertex3f(x2, y2, z2);
-  glTexCoord2f(s3, t3); glVertex3f(x3, y3, z3);
-  glTexCoord2f(s4, t4); glVertex3f(x4, y4, z4);
-  glEnd();
+  GLfloat c[countof(leviathan_color)];
+  memcpy (c, color, sizeof(c));
+  c[3] = c[7] = c[11] = a;
+  set_colors (c);
 }
 
+
 static void
-face5(GLint texture, GLfloat *color, Bool wire,
-      GLfloat s1, GLfloat t1, GLfloat x1, GLfloat y1, GLfloat z1,
-      GLfloat s2, GLfloat t2, GLfloat x2, GLfloat y2, GLfloat z2,
-      GLfloat s3, GLfloat t3, GLfloat x3, GLfloat y3, GLfloat z3,
-      GLfloat s4, GLfloat t4, GLfloat x4, GLfloat y4, GLfloat z4,
-      GLfloat s5, GLfloat t5, GLfloat x5, GLfloat y5, GLfloat z5)
+which_face (ModeInfo *mi, const GLfloat *f, int *face, int *outerp)
 {
-#ifdef HAVE_GLBINDTEXTURE
-  glBindTexture(GL_TEXTURE_2D, texture);
-#endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
-  do_normal(x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
-  glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
-  glTexCoord2f(s1, t1); glVertex3f(x1, y1, z1);
-  glTexCoord2f(s2, t2); glVertex3f(x2, y2, z2);
-  glTexCoord2f(s3, t3); glVertex3f(x3, y3, z3);
-  glTexCoord2f(s4, t4); glVertex3f(x4, y4, z4);
-  glTexCoord2f(s5, t5); glVertex3f(x5, y5, z5);
-  glEnd();
+  GLfloat size = 3;          /* 3" square */
+  const GLfloat *n = f;      /* normal */
+  const GLfloat *v = f + 3;  /* vertex */
+  GLfloat slack = 0.01;
+
+  /* First look at the normal to determine which direction this triangle
+     is facing (or is most-closely facing).
+     It's an outer surface if it is within epsilon of the cube wall that
+     it is facing.  Otherwise, it's an inner surface.
+   */
+  if      (n[1] < -0.5)   *face = 1, *outerp = v[1] < slack;       /* S */
+  else if (n[2] >  0.5)   *face = 2, *outerp = v[2] > size-slack;  /* U */
+  else if (n[1] >  0.5)   *face = 3, *outerp = v[1] > size-slack;  /* N */
+  else if (n[2] < -0.5)   *face = 4, *outerp = v[2] < slack;       /* D */
+  else if (n[0] < -0.5)   *face = 5, *outerp = v[0] < slack;       /* W */
+  else /* (n[0] >  0.5)*/ *face = 6, *outerp = v[0] > size-slack;  /* E */
+
+  /* Faces that don't have normals parallel to the axes aren't external. */
+  if (*outerp &&
+      (n[0] > -0.95 && n[0] < 0.95 &&
+       n[1] > -0.95 && n[1] < 0.95 &&
+       n[2] > -0.95 && n[2] < 0.95))
+    *outerp = 0;
 }
 
 
-\f
-/* Creating object models
- */
-
 static void
-box(ModeInfo *mi, Bool wire)
+texturize_vert (ModeInfo *mi, int which, const GLfloat *v)
 {
-  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+  GLfloat size = 3;          /* 3" square */
+  GLfloat s = 0, q = 0;
+
+  /* Texture coordinates are surface coordinates,
+     on the plane of this cube wall. */
+  switch (which) {
+  case 0: break;
+  case 1: s = v[0], q = v[2]; break;
+  case 2: s = v[0], q = v[1]; break;
+  case 3: s = v[0], q = v[2]; q = size - q; break;
+  case 4: s = v[0], q = v[1]; q = size - q; break;
+  case 5: s = v[1], q = v[2]; break;
+  case 6: s = v[1], q = v[2]; break;
+  default: abort(); break;
+  }
 
-  glNewList(lc->box, GL_COMPILE);
-  glShadeModel(GL_SMOOTH);
-
-  /* N */
-  face4(lc->texids[FACE_N], exterior_color, wire,
-       0.0, 0.0,       -0.5,  0.5,  0.5,
-       1.0, 0.0,        0.5,  0.5,  0.5,
-       1.0, 1.0,        0.5,  0.5, -0.5,
-       0.0, 1.0,       -0.5,  0.5, -0.5);
-
-  /* S */
-  face4(lc->texids[FACE_S], exterior_color, wire,
-       0.0, 0.0,        -0.5, -0.5, -0.5,
-       1.0, 0.0,         0.5, -0.5, -0.5,
-       1.0, 1.0,         0.5, -0.5,  0.5,
-       0.0, 1.0,        -0.5, -0.5,  0.5);
-
-  /* E */
-  face4(lc->texids[FACE_E], exterior_color, wire,
-       0.0, 0.0,        0.5, -0.5, -0.5,
-       1.0, 0.0,        0.5,  0.5, -0.5,
-       1.0, 1.0,        0.5,  0.5,  0.5,
-       0.0, 1.0,        0.5, -0.5,  0.5);
-
-  /* W */
-  face4(lc->texids[FACE_W], exterior_color, wire,
-       1.0, 1.0,       -0.5, -0.5,  0.5,
-       0.0, 1.0,       -0.5,  0.5,  0.5,
-       0.0, 0.0,       -0.5,  0.5, -0.5,
-       1.0, 0.0,       -0.5, -0.5, -0.5);
-
-  /* U */
-  face4(lc->texids[FACE_U], exterior_color, wire,
-       1.0, 0.0,        0.5, -0.5,  0.5,
-       1.0, 1.0,        0.5,  0.5,  0.5,
-       0.0, 1.0,       -0.5,  0.5,  0.5,
-       0.0, 0.0,       -0.5, -0.5,  0.5);
-
-  /* D */
-  face4(lc->texids[FACE_D], exterior_color, wire,
-       0.0, 1.0,       -0.5, -0.5, -0.5,
-       0.0, 0.0,       -0.5,  0.5, -0.5,
-       1.0, 0.0,        0.5,  0.5, -0.5,
-       1.0, 1.0,        0.5, -0.5, -0.5);
-
-  glEndList();
+  glTexCoord2f (s / size, q / size);
 }
 
 
 static void
-star(ModeInfo *mi, Bool top, Bool wire)
+leviathan (ModeInfo *mi, GLfloat ratio, GLfloat alpha, Bool top_p)
 {
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+  Bool wire = MI_IS_WIREFRAME(mi);
+  GLfloat r = 0.34;
+  GLfloat z = 2 * ratio;
+  XYZ p[3];
   int i;
 
-  int points[][2] = {
-    {  77,  74 }, {  60,  98 }, {   0,  71 }, {   0,   0 },    /* L1 */
-    {  60,  98 }, {  55, 127 }, {   0, 127 }, {   0,  71 },    /* L2 */
-    {  55, 127 }, {  60, 154 }, {   0, 179 }, {   0, 127 },    /* L3 */
-    {  60, 154 }, {  76, 176 }, {   0, 255 }, {   0, 179 },    /* L4 */
-    {  76, 176 }, { 100, 193 }, {  74, 255 }, {   0, 255 },    /* B1 */
-    { 100, 193 }, { 127, 198 }, { 127, 255 }, {  74, 255 },    /* B2 */
-    { 127, 198 }, { 151, 193 }, { 180, 255 }, { 127, 255 },    /* B3 */
-    { 151, 193 }, { 178, 177 }, { 255, 255 }, { 180, 255 },    /* B4 */
-    { 178, 177 }, { 193, 155 }, { 255, 181 }, { 255, 255 },    /* R4 */
-    { 193, 155 }, { 199, 127 }, { 255, 127 }, { 255, 181 },    /* R3 */
-    { 199, 127 }, { 194,  99 }, { 255,  74 }, { 255, 127 },    /* R2 */
-    { 194,  99 }, { 179,  76 }, { 255,   0 }, { 255,  74 },    /* R1 */
-    { 179,  76 }, { 155,  60 }, { 180,   0 }, { 255,   0 },    /* T4 */
-    { 155,  60 }, { 126,  55 }, { 126,   0 }, { 180,   0 },    /* T3 */
-    { 126,  55 }, { 100,  60 }, {  75,   0 }, { 126,   0 },    /* T2 */
-    { 100,  60 }, {  77,  74 }, {   0,   0 }, {  75,   0 },    /* T1 */
-  };
-
-  for (i = 0; i < countof(points); i++)
-    points[i][1] = 255-points[i][1];
-
-  if (top)
-    glNewList(lc->star1, GL_COMPILE);
-  else
-    glNewList(lc->star2, GL_COMPILE);
-
-  if (!top)
-    glRotatef(-180.0, 1.0, 0.0, 0.0);
-
-  for (i = 0; i < countof(points)/4; i += 2)
-    {
-      int j, k;
-
-      /* Top face.
-       */
+  GLfloat th = acos (2 / sqrt (6));  /* Line up with cube's diagonal */
 
-      GLfloat s[4], t[4], x[4], y[4], z[4];
-      for (j = 3, k = 0; j >= 0; j--, k++)
-       {
-         GLfloat xx = points[(i*4)+j][0] / 255.0L;
-         GLfloat yy = points[(i*4)+j][1] / 255.0L;
-         s[k] = xx;
-         t[k] = yy;
-         x[k] = xx-0.5;
-         y[k] = yy-0.5;
-         z[k] = 0.5;
-       }
-      face4(lc->texids[top ? FACE_U : FACE_D], exterior_color, wire,
-           s[0], t[0],  x[0], y[0], z[0],
-           s[1], t[1],  x[1], y[1], z[1],
-           s[2], t[2],  x[2], y[2], z[2],
-           s[3], t[3],  x[3], y[3], z[3]);
-
-      /* Bottom face.
-       */
-      for (j = 0, k = 0; j < 4; j++, k++)
-       {
-         GLfloat xx = points[(i*4)+j][0] / 255.0L;
-         GLfloat yy = points[(i*4)+j][1] / 255.0L;
-         s[k] = xx;
-         t[k] = 1.0 - yy;
-         x[k] = xx-0.5;
-         y[k] = yy-0.5;
-         z[k] = -0.5;
-       }
-      face4(lc->texids[top ? FACE_U : FACE_D], exterior_color, wire,
-           s[0], t[0],  x[0], y[0], z[0],
-           s[1], t[1],  x[1], y[1], z[1],
-           s[2], t[2],  x[2], y[2], z[2],
-           s[3], t[3],  x[3], y[3], z[3]);
+  glPushMatrix();
 
-      /* Connecting faces.
-       */
-      for (j = 3; j >= 0; j--)
-       {
-         int k = (j == 0 ? 3 : j-1);
-         Bool front_p = (j == 3);
-         GLfloat x1 = points[(i*4)+j][0] / 255.0L;
-         GLfloat y1 = points[(i*4)+j][1] / 255.0L;
-         GLfloat x2 = points[(i*4)+k][0] / 255.0L;
-         GLfloat y2 = points[(i*4)+k][1] / 255.0L;
-
-         GLfloat tx1=0.0, tx2=1.0, ty1=0.0, ty2=1.0;
-
-         int texture = 0;
-         int facing = i/4;
-         facing = (facing + j + 5) % 4;
-
-         switch (facing) {
-         case 0:
-           texture = FACE_W;
-           if (top) {
-             tx1 = 1.0 - y1;  tx2 = 1.0 - y2;
-             ty1 = 0.0;       ty2 = 1.0;
-           } else {
-             tx1 = y1;  tx2 = y2;
-             ty1 = 1.0; ty2 = 0.0;
-           }
-           break;
-         case 1:
-           texture = top ? FACE_S : FACE_N;
-           tx1 = x1;  tx2 = x2;
-           ty1 = 0.0; ty2 = 1.0;
-           break;
-         case 2:
-           texture = FACE_E;
-           if (top) {
-             tx1 = y1;  tx2 = y2;
-             ty1 = 0.0; ty2 = 1.0;
-           } else {
-             tx1 = 1.0 - y1;  tx2 = 1.0 - y2;
-             ty1 = 1.0;       ty2 = 0.0;
-           }
-           break;
-         case 3:
-           texture = top ? FACE_N : FACE_S;
-           tx1 = x1;  tx2 = x2;
-           ty1 = 1.0; ty2 = 0.0;
-           break;
-         }
-
-         x1 -= 0.5; x2 -= 0.5;
-         y1 -= 0.5; y2 -= 0.5;
-
-         face4(front_p ? lc->texids[texture] : 0,
-               front_p ? exterior_color : interior_color,
-               wire,
-               tx1, ty2,  x1, y1,  0.5,
-               tx1, ty1,  x1, y1, -0.5,
-               tx2, ty1,  x2, y2, -0.5,
-               tx2, ty2,  x2, y2,  0.5);
-       }
-    }
+  glRotatef (-45, 0, 1, 0);
+  glRotatef (-th * 180 / M_PI, 0, 0, 1);
 
+  if (!top_p)
+    glRotatef (180, 0, 0, 1);
 
-  /* Central core top cap.
-   */
-#ifdef HAVE_GLBINDTEXTURE
-  glBindTexture(GL_TEXTURE_2D, lc->texids[top ? FACE_U : FACE_D]);
-#endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
-
-  i = 1;
-  do_normal(points[i+0][0], points[i+0][1], 0,
-           points[i+4][0], points[i+4][1], 0,
-           points[i+8][0], points[i+8][1], 0);
-  glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
-  for (i = 1; i < countof(points); i += 4)
+  for (i = 0; i < countof(p); i++)
     {
-      GLfloat x = points[i][0] / 255.0L;
-      GLfloat y = points[i][1] / 255.0L;
-      glTexCoord2f(x, y);
-      glVertex3f(x-0.5, y-0.5, 0.5);
+      GLfloat th = i * M_PI * 2 / countof(p);
+      p[i].x = cos(th) * r;
+      p[i].y = sin(th) * r;
     }
-  glEnd();
 
+  glFrontFace (GL_CCW);
+  for (i = 0; i < countof(p); i++)
+    {
+      int j = (i + 1) % countof(p);
+/*      if (top_p)*/
+        do_normal (z, 0, 0,
+                   0, p[i].x, p[i].y,
+                   0, p[j].x, p[j].y);
+/*
+      else
+        do_normal (z, 0, 0,
+                   0, p[j].y, p[j].z,
+                   0, p[i].y, p[i].z);
+*/
 
-  /* Central core bottom cap.
-   */
-#ifdef HAVE_GLBINDTEXTURE
-  glBindTexture(GL_TEXTURE_2D, 0);
-#endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, interior_color);
+      if (do_texture)  /* Leviathan is the final texture */
+        glBindTexture (GL_TEXTURE_2D, lc->texids[countof(lc->texids) - 1]);
 
-  i = countof(points) - 3;
-  do_normal(points[i+0][0], points[i+0][1], 0,
-           points[i+4][0], points[i+4][1], 0,
-           points[i+8][0], points[i+8][1], 0);
+      set_colors (leviathan_color);
 
-  glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
-  for (i = countof(points) - 3; i >= 0; i -= 4)
-    {
-      GLfloat x = points[i][0] / 255.0L;
-      GLfloat y = points[i][1] / 255.0L;
-      glVertex3f(x-0.5, y-0.5, 0);
-    }
-  glEnd();
-  
+      glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
+      glTexCoord2f (0.5, 1);
+      glVertex3f (z, 0, 0);
 
-  /* Central core walls.
-   */
-  for (i = 1; i < countof(points); i += 4)
-    {
+      glTexCoord2f (0, 0);
+      glVertex3f (0, p[i].x, p[i].y);
 
-      GLfloat x1 = points[i-1][0] / 255.0L;
-      GLfloat y1 = points[i-1][1] / 255.0L;
-      GLfloat x2 = points[i][0] / 255.0L;
-      GLfloat y2 = points[i][1] / 255.0L;
-      face4(0, interior_color, wire,
-           0.0, 0.0,  x1-0.5, y1-0.5, 0.5,
-           0.0, 0.0,  x1-0.5, y1-0.5, 0.0,
-           0.0, 0.0,  x2-0.5, y2-0.5, 0.0,
-           0.0, 0.0,  x2-0.5, y2-0.5, 0.5);
+      glTexCoord2f (1, 0);
+      glVertex3f (0, p[j].x, p[j].y);
+      glEnd();
+      mi->polygon_count++;
+
+      /* Shield for fading */
+      if (alpha < 0.9 && !wire)
+        {
+          GLfloat a = 0.35;
+          GLfloat b = 0.69;
+
+          set_colors_alpha (black_color, 1-alpha);
+          glBindTexture (GL_TEXTURE_2D, 0);
+          if (!wire)
+            {
+              glEnable (GL_BLEND);
+              glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+            }
+
+          glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+
+          glVertex3f (z*a, p[j].x * b, p[j].y * b);
+          glVertex3f (z*a, p[i].x * b, p[i].y * b);
+          glVertex3f (0, p[i].x * 1.01, p[i].y * 1.01);
+          glVertex3f (0, p[j].x * 1.01, p[j].y * 1.01);
+          glEnd();
+          mi->polygon_count++;
+          glDisable (GL_BLEND);
+        }
     }
 
-  glEndList();
+  glPopMatrix();
 }
 
 
 static void
-tetra(ModeInfo *mi, Bool wire)
-{
-  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
-
-  glNewList(lc->tetra_une, GL_COMPILE);
-  {
-    glShadeModel(GL_SMOOTH);
-
-    /* Ua */
-    face3(lc->texids[FACE_U], exterior_color, wire,
-         1.0, 0.0,      0.5, -0.5,  0.5,
-         1.0, 1.0,      0.5,  0.5,  0.5,
-         0.0, 1.0,     -0.5,  0.5,  0.5);
-
-    /* Na */
-    face3(lc->texids[FACE_N], exterior_color, wire,
-         0.0, 0.0,     -0.5,  0.5,  0.5,
-         1.0, 0.0,      0.5,  0.5,  0.5,
-         1.0, 1.0,      0.5,  0.5, -0.5);
-
-    /* Eb */
-    face3(lc->texids[FACE_E], exterior_color, wire,
-         1.0, 0.0,      0.5,  0.5, -0.5,
-         1.0, 1.0,      0.5,  0.5,  0.5,
-         0.0, 1.0,      0.5, -0.5,  0.5);
-
-    face3(0, interior_color, wire,
-         0.0, 0.0,      0.5,  0.5, -0.5,
-         0.0, 0.0,      0.5, -0.5,  0.5,
-         0.0, 0.0,     -0.5,  0.5,  0.5);
-  }
-  glEndList();
-
-  glNewList(lc->tetra_usw, GL_COMPILE);
-  {
-    /* Ub */
-    face3(lc->texids[FACE_U], exterior_color, wire,
-         0.0, 1.0,     -0.5,  0.5,  0.5,
-         0.0, 0.0,     -0.5, -0.5,  0.5,
-         1.0, 0.0,      0.5, -0.5,  0.5);
-
-    /* Sb */
-    face3(lc->texids[FACE_S], exterior_color, wire,
-         1.0, 1.0,      0.5, -0.5,  0.5,
-         0.0, 1.0,     -0.5, -0.5,  0.5,
-         0.0, 0.0,     -0.5, -0.5, -0.5);
-
-    /* Wb */
-    face3(lc->texids[FACE_W], exterior_color, wire,
-         1.0, 0.0,     -0.5, -0.5, -0.5,
-         1.0, 1.0,     -0.5, -0.5,  0.5,
-         0.0, 1.0,     -0.5,  0.5,  0.5);
-
-    face3(0, interior_color, wire,
-         0.0,0.0,      -0.5, -0.5, -0.5,
-         0.0,0.0,      -0.5,  0.5,  0.5,
-         0.0,0.0,       0.5, -0.5,  0.5);
-  }
-  glEndList();
-
-  glNewList(lc->tetra_dwn, GL_COMPILE);
-  {
-    /* Db */
-    face3(lc->texids[FACE_D], exterior_color, wire,
-         0.0, 1.0,     -0.5, -0.5, -0.5,
-         0.0, 0.0,     -0.5,  0.5, -0.5,
-         1.0, 0.0,      0.5,  0.5, -0.5);
-
-    /* Wa */
-    face3(lc->texids[FACE_W], exterior_color, wire,
-         0.0, 1.0,     -0.5,  0.5,  0.5,
-         0.0, 0.0,     -0.5,  0.5, -0.5,
-         1.0, 0.0,     -0.5, -0.5, -0.5);
-
-    /* Nb */
-    face3(lc->texids[FACE_N], exterior_color, wire,
-         1.0, 1.0,      0.5,  0.5, -0.5,
-         0.0, 1.0,     -0.5,  0.5, -0.5,
-         0.0, 0.0,     -0.5,  0.5,  0.5);
-
-    face3(0, interior_color, wire,
-         0.0, 0.0,      0.5,  0.5, -0.5,
-         0.0, 0.0,     -0.5,  0.5,  0.5,
-         0.0, 0.0,     -0.5, -0.5, -0.5);
-  }
-  glEndList();
-
-  glNewList(lc->tetra_dse, GL_COMPILE);
-  {
-    /* Sa */
-    face3(lc->texids[FACE_S], exterior_color, wire,
-         0.0, 0.0,     -0.5, -0.5, -0.5,
-         1.0, 0.0,      0.5, -0.5, -0.5,
-         1.0, 1.0,      0.5, -0.5,  0.5);
-
-    /* Ea */
-    face3(lc->texids[FACE_E], exterior_color, wire,
-         0.0, 1.0,      0.5, -0.5,  0.5,
-         0.0, 0.0,      0.5, -0.5, -0.5,
-         1.0, 0.0,      0.5,  0.5, -0.5);
-
-    /* Da */
-    face3(lc->texids[FACE_D], exterior_color, wire,
-         1.0, 0.0,      0.5,  0.5, -0.5,
-         1.0, 1.0,      0.5, -0.5, -0.5,
-         0.0, 1.0,     -0.5, -0.5, -0.5);
-
-    face3(0, interior_color, wire,
-         0.0, 0.0,      0.5, -0.5,  0.5,
-         0.0, 0.0,      0.5,  0.5, -0.5,
-         0.0, 0.0,     -0.5, -0.5, -0.5);
-    glEnd();
-  }
-  glEndList();
-
-  glNewList(lc->tetra_mid, GL_COMPILE);
-  {
-    face3(0, interior_color, wire,
-         0.0, 0.0,      0.5, -0.5,  0.5,
-         0.0, 0.0,      0.5,  0.5, -0.5,
-         0.0, 0.0,     -0.5,  0.5,  0.5);
-
-    face3(0, interior_color, wire,
-         0.0, 0.0,     -0.5,  0.5,  0.5,
-         0.0, 0.0,     -0.5, -0.5, -0.5,
-         0.0, 0.0,      0.5, -0.5,  0.5);
-
-    face3(0, interior_color, wire,
-         0.0, 0.0,     -0.5,  0.5,  0.5,
-         0.0, 0.0,      0.5,  0.5, -0.5,
-         0.0, 0.0,     -0.5, -0.5, -0.5);
-
-    face3(0, interior_color, wire,
-         0.0, 0.0,      0.5,  0.5, -0.5,
-         0.0, 0.0,      0.5, -0.5,  0.5,
-         0.0, 0.0,     -0.5, -0.5, -0.5);
-
-    face3(0, interior_color, wire,
-         0.0, 0.0,      0.5, -0.5,  0.5,
-         0.0, 0.0,      0.5,  0.5, -0.5,
-         0.0, 0.0,     -0.5, -0.5, -0.5);
-  }
-  glEndList();
-
-}
-
-static void
-lid(ModeInfo *mi, Bool wire)
+folding_walls (ModeInfo *mi, GLfloat ratio, Bool top_p)
 {
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+  Bool wire = MI_IS_WIREFRAME(mi);
+  const GLfloat pa[4][2] = {{ -0.5,      -0.215833 },
+                            {  0,         0.5      },
+                            {  0.5,       0        },
+                            { -0.215833, -0.5      }};
+  const int tex[6] = { 0, 5, 1,  4, 2, 3 };
+  const GLfloat top = -pa[0][1];
+  GLfloat end_angle = 30.85;
+  GLfloat rr = sin (ratio / 2 * M_PI);
+  GLfloat offa = 0.15 * rr;
+  GLfloat offb = 0.06 * rr;
+  GLfloat p[4][3];
+  GLfloat t[4][2];
   int i;
 
-  int points[][2] = {
-    { 128,  20 },{  21, 129 },{   0, 129 },{   0,   0 },{ 128,   0 }, /* L1 */
-    {  21, 129 },{ 127, 234 },{ 127, 255 },{   0, 255 },{   0, 129 }, /* L2 */
-    { 127, 234 },{ 233, 127 },{ 255, 127 },{ 255, 255 },{ 127, 255 }, /* R2 */
-    { 233, 127 },{ 128,  20 },{ 128,   0 },{ 255,   0 },{ 255, 127 }, /* R1 */
-  };
-
-  for (i = 0; i < countof(points); i++)
-    points[i][1] = 255-points[i][1];
-
-  glNewList(lc->lid_0, GL_COMPILE);
-  glShadeModel(GL_SMOOTH);
-
-  /* N */
-  face4(lc->texids[FACE_N], exterior_color, wire,
-       0.0, 0.0,       -0.5,  0.5,  0.5,
-       1.0, 0.0,        0.5,  0.5,  0.5,
-       1.0, 1.0,        0.5,  0.5, -0.5,
-       0.0, 1.0,       -0.5,  0.5, -0.5);
-
-  /* S */
-  face4(lc->texids[FACE_S], exterior_color, wire,
-       0.0, 0.0,       -0.5, -0.5, -0.5,
-       1.0, 0.0,        0.5, -0.5, -0.5,
-       1.0, 1.0,        0.5, -0.5,  0.5,
-       0.0, 1.0,       -0.5, -0.5,  0.5);
-
-  /* E */
-  face4(lc->texids[FACE_E], exterior_color, wire,
-       0.0, 0.0,        0.5, -0.5, -0.5,
-       1.0, 0.0,        0.5,  0.5, -0.5,
-       1.0, 1.0,        0.5,  0.5,  0.5,
-       0.0, 1.0,        0.5, -0.5,  0.5);
-
-  /* U */
-  face4(lc->texids[FACE_U], exterior_color, wire,
-       1.0, 0.0,        0.5, -0.5,  0.5,
-       1.0, 1.0,        0.5,  0.5,  0.5,
-       0.0, 1.0,       -0.5,  0.5,  0.5,
-       0.0, 0.0,       -0.5, -0.5,  0.5);
-
-  /* D */
-  face4(lc->texids[FACE_D], exterior_color, wire,
-       0.0, 1.0,       -0.5, -0.5, -0.5,
-       0.0, 0.0,       -0.5,  0.5, -0.5,
-       1.0, 0.0,        0.5,  0.5, -0.5,
-       1.0, 1.0,        0.5, -0.5, -0.5);
-
-  /* W -- lid_0 */
-  for (i = 0; i < countof(points)/5; i++)
-    {
-      int j;
-      GLfloat s[5], t[5], x[5], y[5], z[5];
-      for (j = 0; j < 5; j++)
-       {
-         GLfloat xx = points[(i*5)+j][0] / 255.0L;
-         GLfloat yy = points[(i*5)+j][1] / 255.0L;
-         s[j] = 1.0-xx;
-         t[j] = yy;
-         x[j] = -0.5;
-         y[j] = xx-0.5;
-         z[j] = yy-0.5;
-       }
-      face5(lc->texids[FACE_W], exterior_color, wire,
-           s[0], t[0],  x[0], y[0], z[0],
-           s[1], t[1],  x[1], y[1], z[1],
-           s[2], t[2],  x[2], y[2], z[2],
-           s[3], t[3],  x[3], y[3], z[3],
-           s[4], t[4],  x[4], y[4], z[4]);
-    }
-  glEndList();
-
+  glPushMatrix();
 
-  /* W -- lid_1 through lid_4 */
-  for (i = 0; i < 4; i++)
+  if (top_p)
     {
-      GLfloat x1, y1, x2, y2, x3, y3;
-
-      glNewList(lc->lid_1 + i, GL_COMPILE);
-      glShadeModel(GL_SMOOTH);
-
-      x1 = points[(i*5)+1][0] / 255.0L;
-      y1 = points[(i*5)+1][1] / 255.0L;
-      x2 = points[(i*5)][0] / 255.0L;
-      y2 = points[(i*5)][1] / 255.0L;
-      x3 = 0.5;
-      y3 = 0.5;
-
-      /* Outer surface */
-      face3(lc->texids[FACE_W], exterior_color, wire,
-           1.0-x1, y1,         -0.5, x1-0.5, y1-0.5,
-           1.0-x2, y2,         -0.5, x2-0.5, y2-0.5,
-           1.0-x3, y3,         -0.5, x3-0.5, y3-0.5);
-
-      /* Inner surface */
-      face3(0, interior_color, wire,
-           0.0, 0.0,   -0.48, x2-0.5, y2-0.5,
-           0.0, 0.0,   -0.48, x1-0.5, y1-0.5,
-           0.0, 0.0,   -0.48, x3-0.5, y3-0.5);
-
-      /* Lip 1 */
-      face4(0, interior_color, wire,
-           0.0, 0.0,   -0.5,  x1-0.5, y1-0.5,
-           0.0, 0.0,   -0.5,  x3-0.5, y3-0.5,
-           0.0, 0.0,   -0.48, x3-0.5, y3-0.5,
-           0.0, 0.0,   -0.48, x1-0.5, y1-0.5);
-
-      /* Lip 2 */
-      face4(0, interior_color, wire,
-           0.0, 0.0,   -0.48, x2-0.5, y2-0.5,
-           0.0, 0.0,   -0.48, x3-0.5, y3-0.5,
-           0.0, 0.0,   -0.5,  x3-0.5, y3-0.5,
-           0.0, 0.0,   -0.5,  x2-0.5, y2-0.5);
-
-      glEndList();
+      glRotatef (60, 1, -1, 1);
+      glRotatef (180, 0, 1, 0);
+      glRotatef (90, 1, 0, 0);
     }
-}
-
-static void
-taser(ModeInfo *mi, Bool wire)
-{
-  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
-  int i;
-
-  int slider_face_points[][2] = {
-    {  86,  58 },{  38, 106 },{  70, 106 },{ 118,  58 },{  -1,  -1 }, /* a */
-    { 136,  58 },{ 184, 106 },{ 216, 106 },{ 168,  58 },{  -1,  -1 }, /* b */
-    {  38, 106 },{   0, 144 },{   0, 190 },{  60, 190 },{ 108, 106 }, /* c */
-    { 144, 106 },{ 194, 190 },{ 254, 190 },{ 254, 144 },{ 216, 106 }, /* d */
-    {  98, 124 },{  60, 190 },{  92, 190 },{ 126, 158 },{ 126, 124 }, /* e */
-    { 126, 124 },{ 126, 158 },{ 160, 190 },{ 194, 190 },{ 154, 124 }, /* f */
-    {  22, 190 },{  22, 254 },{  60, 254 },{  60, 190 },{  -1,  -1 }, /* g */
-    { 194, 190 },{ 194, 254 },{ 230, 254 },{ 230, 190 },{  -1,  -1 }, /* h */
-    {  60, 190 },{  60, 210 },{  92, 210 },{  92, 190 },{  -1,  -1 }, /* i */
-    { 160, 190 },{ 160, 210 },{ 194, 210 },{ 194, 190 },{  -1,  -1 }, /* j */
-    { 110, 172 },{  92, 190 },{ 110, 190 },{  -1,  -1 },{  -1,  -1 }, /* k */
-    { 140, 172 },{ 140, 190 },{ 160, 190 },{  -1,  -1 },{  -1,  -1 }, /* l */
-    { 110, 172 },{ 140, 172 },{ 126, 156 },{  -1,  -1 },{  -1,  -1 }, /* m */
-  };
-
-  int body_face_points[][2] = {
-    {   0,   0 },{   0,  58 },{ 254,  58 },{ 254,   0 },{  -1,  -1 }, /* A */
-    {   0,  58 },{   0, 144 },{  86,  58 },{  -1,  -1 },{  -1,  -1 }, /* B */
-    { 168,  58 },{ 254, 144 },{ 254,  58 },{  -1,  -1 },{  -1,  -1 }, /* C */
-    { 118,  58 },{  70, 106 },{ 184, 106 },{ 136,  58 },{  -1,  -1 }, /* F */
-    { 108, 106 },{  98, 124 },{ 154, 124 },{ 144, 106 },{  -1,  -1 }, /* G */
-  };
-
-  int lifter_face_points[][2] = {
-    {   0, 190 },{   0, 254 },{  22, 254 },{  22, 190 },{  -1,  -1 }, /* D */
-    { 230, 190 },{ 230, 254 },{ 254, 254 },{ 254, 190 },{  -1,  -1 }, /* E */
-    {  60, 210 },{  60, 254 },{ 194, 254 },{ 194, 210 },{  -1,  -1 }, /* H */
-    {  92, 190 },{  92, 210 },{ 160, 210 },{ 160, 190 },{  -1,  -1 }, /* I */
-    { 110, 172 },{ 110, 190 },{ 140, 190 },{ 140, 172 },{  -1,  -1 }, /* J */
-  };
-
-  int body_perimiter_points[][2] = {
-    {   0, 144 },{  86,  59 },{ 119,  58 },{  71, 107 },
-    { 108, 107 },{  98, 124 },{ 155, 124 },{ 144, 107 },
-    { 185, 106 },{ 136,  59 },{ 169,  59 },{ 255, 145 },
-    { 255,   0 },{   0,   0 },
-  };
-
-  int slider_perimiter_points[][2] = {
-    {  86,  58 },{   0,  144 },{   0, 190 },{  22,  190 },{  22, 254 },
-    {  60, 254 },{  60,  210 },{  92, 210 },{  92,  190 },{ 110, 190 },
-    { 110, 172 },{  140, 172 },{ 140, 190 },{ 160,  190 },{ 160, 210 },
-    { 194, 210 },{  194, 254 },{ 230, 254 },{ 230,  190 },{ 254, 190 },
-    { 254, 144 },{  168,  58 },{ 136,  58 },{ 184,  106 },{ 144, 106 },
-    { 154, 124 },{  98,  124 },{ 108, 106 },{  70,  106 },{ 118,  58 },
-  };
-
-  int lifter_perimiter_points_1[][2] = {
-    {   0, 189 },{   0, 254 },{  22, 255 },{  23, 190 },
-  };
-
-  int lifter_perimiter_points_2[][2] = {
-    { 230, 254 },{ 255, 255 },{ 254, 190 },{ 230, 190 },
-  };
-
-  int lifter_perimiter_points_3[][2] = {
-    {  60, 254 },{ 194, 254 },{ 194, 211 },{ 160, 210 },
-    { 160, 190 },{ 140, 191 },{ 141, 172 },{ 111, 172 },
-    { 110, 190 },{ 93, 190 },{ 92, 210 },{ 60, 211 },
-  };
-
-  for (i = 0; i < countof(slider_face_points); i++)
-    slider_face_points[i][1] = 255-slider_face_points[i][1];
-  for (i = 0; i < countof(body_face_points); i++)
-    body_face_points[i][1] = 255-body_face_points[i][1];
-  for (i = 0; i < countof(lifter_face_points); i++)
-    lifter_face_points[i][1] = 255-lifter_face_points[i][1];
-  for (i = 0; i < countof(body_perimiter_points); i++)
-    body_perimiter_points[i][1] = 255-body_perimiter_points[i][1];
-  for (i = 0; i < countof(slider_perimiter_points); i++)
-    slider_perimiter_points[i][1] = 255-slider_perimiter_points[i][1];
-  for (i = 0; i < countof(lifter_perimiter_points_1); i++)
-    lifter_perimiter_points_1[i][1] = 255-lifter_perimiter_points_1[i][1];
-  for (i = 0; i < countof(lifter_perimiter_points_2); i++)
-    lifter_perimiter_points_2[i][1] = 255-lifter_perimiter_points_2[i][1];
-  for (i = 0; i < countof(lifter_perimiter_points_3); i++)
-    lifter_perimiter_points_3[i][1] = 255-lifter_perimiter_points_3[i][1];
-
-  /* -------------------------------------------------------------------- */
-
-  glNewList(lc->taser_base, GL_COMPILE);
-  glShadeModel(GL_SMOOTH);
-
-  /* N */
-  face4(lc->texids[FACE_N], exterior_color, wire,
-       0.0, 0.0,       -0.5,  0.5,  0.5,
-       0.75, 0.0,       0.25, 0.5,  0.5,
-       0.75, 0.75,      0.25, 0.5, -0.25,
-       0.0, 0.75,      -0.5,  0.5, -0.25);
-
-  /* S */
-  face4(lc->texids[FACE_S], exterior_color, wire,
-       0.0,  0.25,     -0.5,  -0.5, -0.25,
-       0.75, 0.25,      0.25, -0.5, -0.25,
-       0.75, 1.0,       0.25, -0.5,  0.5,
-       0.0,  1.0,      -0.5,  -0.5,  0.5);
-
-  /* interior E */
-  face4(0, interior_color, wire,
-       0.0, 0.0,        0.25, -0.5, -0.25,
-       1.0, 0.0,        0.25,  0.5, -0.25,
-       1.0, 1.0,        0.25,  0.5,  0.5,
-       0.0, 1.0,        0.25, -0.5,  0.5);
-
-  /* W */
-  face4(lc->texids[FACE_W], exterior_color, wire,
-       1.0, 1.0,       -0.5, -0.5,  0.5,
-       0.0, 1.0,       -0.5,  0.5,  0.5,
-       0.0, 0.25,      -0.5,  0.5, -0.25,
-       1.0, 0.25,      -0.5, -0.5, -0.25);
-
-  /* U */
-  face4(lc->texids[FACE_U], exterior_color, wire,
-       0.75, 0.0,       0.25, -0.5,  0.5,
-       0.75, 1.0,       0.25,  0.5,  0.5,
-       0.0, 1.0,       -0.5,   0.5,  0.5,
-       0.0, 0.0,       -0.5,  -0.5,  0.5);
-
-  /* interior D */
-  face4(0, interior_color, wire,
-       0.0, 1.0,       -0.5,  -0.5, -0.25,
-       0.0, 0.0,       -0.5,   0.5, -0.25,
-       1.0, 0.0,        0.25,  0.5, -0.25,
-       1.0, 1.0,        0.25, -0.5, -0.25);
-
-  /* Top face */
-  for (i = 0; i < countof(body_face_points)/5; i++)
+  else
     {
-      int j;
-#ifdef HAVE_GLBINDTEXTURE
-      glBindTexture(GL_TEXTURE_2D, lc->texids[FACE_E]);
-#endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
-
-      do_normal(0, body_face_points[(i*5)+0][0], body_face_points[(i*5)+0][1],
-               0, body_face_points[(i*5)+1][0], body_face_points[(i*5)+1][1],
-               0, body_face_points[(i*5)+2][0], body_face_points[(i*5)+2][1]
-               );
-      glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
-      for (j = 0; j < 5; j++)
-       {
-         int ix = body_face_points[(i*5)+j][0];
-         int iy = body_face_points[(i*5)+j][1];
-         GLfloat x, y;
-         if (ix == -1)  /* these are padding: ignore them */
-           continue;
-         x = ix / 255.0L;
-         y = iy / 255.0L;
-         glTexCoord2f(x, y);
-         glVertex3f(0.5, x-0.5, y-0.5);
-       }
-      glEnd();
+      glRotatef (180, 1, 0, 0);
     }
 
-  /* Side walls */
-  for (i = 0; i < countof(body_perimiter_points); i++)
-    {
-      int j = (i+1 >= countof(body_perimiter_points) ? 0 : i+1);
-      GLfloat x1 = body_perimiter_points[i][0] / 255.0;
-      GLfloat y1 = body_perimiter_points[i][1] / 255.0;
-      GLfloat x2 = body_perimiter_points[j][0] / 255.0;
-      GLfloat y2 = body_perimiter_points[j][1] / 255.0;
-      int texture = -1;
-      GLfloat s1=0, t1=0, s2=0, t2=0, s3=0, t3=0, s4=0, t4=0;
-
-      if (i == 11)
-       {
-         texture = lc->texids[FACE_N];
-         s1 = 1.0;  t1 = 0.0;
-         s2 = 1.0;  t2 = 0.568;
-         s3 = 0.75, t3 = 0.568;
-         s4 = 0.75; t4 = 0.0;
-       }
-      else if (i == 12)
-       {
-         texture = lc->texids[FACE_U];
-         s1 = 1.0;  t1 = 0.0;
-         s2 = 1.0;  t2 = 1.0;
-         s3 = 0.75, t3 = 1.0;
-         s4 = 0.75; t4 = 0.0;
-       }
-      else if (i == 13)
-       {
-         texture = lc->texids[FACE_S];
-         s1 = 1.0;  t1 = 0.437;
-         s2 = 1.0;  t2 = 1.0;
-         s3 = 0.75; t3 = 1.0;
-         s4 = 0.75; t4 = 0.437;
-       }
-
-      face4((texture == -1 ? 0 : texture),
-           (texture == -1 ? interior_color : exterior_color),
-           wire,
-           s1, t1,  0.5,  x2-0.5, y2-0.5,
-           s2, t2,  0.5,  x1-0.5, y1-0.5,
-           s3, t3,  0.25, x1-0.5, y1-0.5,
-           s4, t4,  0.25, x2-0.5, y2-0.5);
-    }
+  /* Scale down the points near the axis */
 
-  glEndList();
-
-  /* -------------------------------------------------------------------- */
-
-  glNewList(lc->taser_lifter, GL_COMPILE);
-  glShadeModel(GL_SMOOTH);
-
-  /* N */
-  face4(lc->texids[FACE_N], exterior_color, wire,
-       0.0,  0.75,     -0.5,  0.5, -0.25,
-       0.75, 0.75,      0.25, 0.5, -0.25,
-       0.75, 1.0,       0.25, 0.5, -0.5,
-       0.0,  1.0,      -0.5,  0.5, -0.5);
-
-  /* S */
-  face4(lc->texids[FACE_S], exterior_color, wire,
-       0.0,  0.0,      -0.5,  -0.5, -0.5,
-       0.75, 0.0,       0.25, -0.5, -0.5,
-       0.75, 0.25,      0.25, -0.5, -0.25,
-       0.0,  0.25,     -0.5,  -0.5, -0.25);
-
-  /* interior E */
-  face4(0, interior_color, wire,
-       0.0, 1.0,        0.25, -0.5, -0.5,
-       1.0, 1.0,        0.25,  0.5, -0.5,
-       1.0, 0.0,        0.25,  0.5, -0.25,
-       0.0, 0.0,        0.25, -0.5, -0.25);
-
-  /* W */
-  face4(lc->texids[FACE_W], exterior_color, wire,
-       1.0, 0.25,      -0.5, -0.5, -0.25,
-       0.0, 0.25,      -0.5,  0.5, -0.25,
-       0.0, 0.0,       -0.5,  0.5, -0.5,
-       1.0, 0.0,       -0.5, -0.5, -0.5);
-
-  /* interior U */
-  face4(0, interior_color, wire,
-       1.0, 0.0,        0.25, -0.5,  -0.25,
-       1.0, 1.0,        0.25,  0.5,  -0.25,
-       0.0, 1.0,       -0.5,   0.5,  -0.25,
-       0.0, 0.0,       -0.5,  -0.5,  -0.25);
-
-  /* D */
-  face4(lc->texids[FACE_D], exterior_color, wire,
-       0.0, 1.0,       -0.5, -0.5, -0.5,
-       0.0, 0.0,       -0.5,  0.5, -0.5,
-       0.75, 0.0,       0.25,  0.5, -0.5,
-       0.75, 1.0,       0.25, -0.5, -0.5);
-
-
-  /* Top face */
-  for (i = 0; i < countof(lifter_face_points)/5; i++)
-    {
-      int j;
+  p[0][0] = pa[0][0];
+  p[0][1] = 0.5;
+  p[0][2] = pa[0][1];
 
-#ifdef HAVE_GLBINDTEXTURE
-      glBindTexture(GL_TEXTURE_2D, lc->texids[FACE_E]);
-#endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
+  p[1][0] = pa[1][0] - offb;
+  p[1][1] = 0.5;
+  p[1][2] = pa[1][1] - offa;
 
-      do_normal(
-         0, lifter_face_points[(i*5)+0][0], lifter_face_points[(i*5)+0][1],
-        0, lifter_face_points[(i*5)+1][0], lifter_face_points[(i*5)+1][1],
-        0, lifter_face_points[(i*5)+2][0], lifter_face_points[(i*5)+2][1]);
+  p[2][0] = pa[2][0] - offa;
+  p[2][1] = 0.5;
+  p[2][2] = pa[2][1] - offb;
 
-      glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
-      for (j = 0; j < 5; j++)
-       {
-         int ix = lifter_face_points[(i*5)+j][0];
-         int iy = lifter_face_points[(i*5)+j][1];
-         GLfloat x, y;
-         if (ix == -1)  /* these are padding: ignore them */
-           continue;
-         x = ix / 255.0L;
-         y = iy / 255.0L;
-         glTexCoord2f(x, y);
-         glVertex3f(0.5, x-0.5, y-0.5);
-       }
-      glEnd();
-    }
+  p[3][0] = pa[3][0];
+  p[3][1] = 0.5;
+  p[3][2] = pa[3][1];
 
-  /* Side walls */
-  for (i = 0; i < countof(lifter_perimiter_points_1); i++)
+  if (!wire)
     {
-      int j = (i+1 >= countof(lifter_perimiter_points_1) ? 0 : i+1);
-      GLfloat x1 = lifter_perimiter_points_1[i][0] / 255.0;
-      GLfloat y1 = lifter_perimiter_points_1[i][1] / 255.0;
-      GLfloat x2 = lifter_perimiter_points_1[j][0] / 255.0;
-      GLfloat y2 = lifter_perimiter_points_1[j][1] / 255.0;
-      int texture = -1;
-      GLfloat s1=0, t1=0, s2=0, t2=0, s3=0, t3=0, s4=0, t4=0;
-
-      if (i == 0)
-       {
-         texture = lc->texids[FACE_S];
-         s1 = 1.0;  t1 = 0.0;
-         s2 = 1.0;  t2 = 0.26;
-         s3 = 0.75, t3 = 0.26;
-         s4 = 0.75; t4 = 0.0;
-       }
-      else if (i == 1)
-       {
-         texture = lc->texids[FACE_D];
-         s1 = 1.0;  t1 = 0.914;
-         s2 = 1.0;  t2 = 1.0;
-         s3 = 0.75; t3 = 1.0;
-         s4 = 0.75; t4 = 0.914;
-       }
-
-      face4((texture == -1 ? 0 : texture),
-           (texture == -1 ? interior_color : exterior_color),
-           wire,
-           s1, t1,  0.5,  x2-0.5, y2-0.5,
-           s2, t2,  0.5,  x1-0.5, y1-0.5,
-           s3, t3,  0.25, x1-0.5, y1-0.5,
-           s4, t4,  0.25, x2-0.5, y2-0.5);
+      glEnable (GL_BLEND);
+      glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     }
 
-  for (i = 0; i < countof(lifter_perimiter_points_2); i++)
+  for (i = 0; i < 3; i++)
     {
-      int j = (i+1 >= countof(lifter_perimiter_points_2) ? 0 : i+1);
-      GLfloat x1 = lifter_perimiter_points_2[i][0] / 255.0;
-      GLfloat y1 = lifter_perimiter_points_2[i][1] / 255.0;
-      GLfloat x2 = lifter_perimiter_points_2[j][0] / 255.0;
-      GLfloat y2 = lifter_perimiter_points_2[j][1] / 255.0;
-      int texture = -1;
-      GLfloat s1=0, t1=0, s2=0, t2=0, s3=0, t3=0, s4=0, t4=0;
-
-      if (i == 0)
-       {
-         texture = lc->texids[FACE_D];
-         s1 = 1.0;  t1 = 0.0;
-         s2 = 1.0;  t2 = 0.095;
-         s3 = 0.75; t3 = 0.095;
-         s4 = 0.75; t4 = 0.0;
-       }
-      else if (i == 1)
-       {
-         texture = lc->texids[FACE_N];
-         s1 = 1.0;  t1 = 0.745;
-         s2 = 1.0;  t2 = 1.0;
-         s3 = 0.75; t3 = 1.0;
-         s4 = 0.75; t4 = 0.745;
-       }
+      glPushMatrix();
 
-      face4((texture == -1 ? 0 : texture),
-           (texture == -1 ? interior_color : exterior_color),
-           wire,
-           s1, t1,  0.5,  x2-0.5, y2-0.5,
-           s2, t2,  0.5,  x1-0.5, y1-0.5,
-           s3, t3,  0.25, x1-0.5, y1-0.5,
-           s4, t4,  0.25, x2-0.5, y2-0.5);
+      if (i == 1)
+        {
+          glRotatef (-90, 1, 0, 0);
+          glRotatef (180, 1, 1, 0);
+        }
+      else if (i == 2)
+        {
+          glRotatef (-90, 1, 0, 0);
+          glRotatef (180, 0, 1, 0);
+          glRotatef (90, 0, 1, 0);
+        }
+
+      glRotatef (-90, 0, 1, 0);
+
+      glTranslatef (-(top/2 + 0.25), 0.5, -(top/2 + 0.25));
+      glRotatef (-45, 0, 1, 0);
+      glRotatef (ratio * -end_angle, 0, 0, 1);
+      glRotatef (45, 0, 1, 0);
+      glTranslatef (top/2 + 0.25, -0.5, top/2 + 0.25);
+
+      /* Get the texture coordinates right.
+         This is hairy and incomprehensible. */
+
+      t[0][0] = pa[0][1] + 0.5; t[0][1] = pa[0][0] + 0.5;
+      t[1][0] = pa[1][1] + 0.5; t[1][1] = pa[1][0] + 0.5;
+      t[2][0] = pa[2][1] + 0.5; t[2][1] = pa[2][0] + 0.5;
+      t[3][0] = pa[3][1] + 0.5; t[3][1] = pa[3][0] + 0.5;
+
+      if (i == 0 && !top_p)
+        {
+# define SWAP(A,B) A = 1-A, B = 1-B
+          SWAP(t[0][0], t[0][1]);
+          SWAP(t[1][0], t[1][1]);
+          SWAP(t[2][0], t[2][1]);
+          SWAP(t[3][0], t[3][1]);
+# undef SWAP
+        }
+      else if (i == 0 && top_p)
+        {
+          GLfloat ot[4][3];
+          memcpy (ot, t, sizeof(t));
+# define SWAP(A,B) A = 1-A, B = 1-B
+          SWAP(t[0][0], ot[2][1]);
+          SWAP(t[1][0], ot[3][1]);
+          SWAP(t[2][0], ot[0][1]);
+          SWAP(t[3][0], ot[1][1]);
+# undef SWAP
+        }
+      else if (i == 1)
+        {
+          GLfloat f;
+# define SWAP(A,B) f = A, A = B, B = -f
+          SWAP(t[0][0], t[0][1]);
+          SWAP(t[1][0], t[1][1]);
+          SWAP(t[2][0], t[2][1]);
+          SWAP(t[3][0], t[3][1]);
+# undef SWAP
+        }
+
+      set_colors_alpha (exterior_color, 1-ratio);
+      glBindTexture (GL_TEXTURE_2D, lc->texids[tex[i + (top_p ? 3 : 0)]]);
+
+      glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+      do_normal (p[0][0], p[0][1], p[0][2],
+                 p[1][0], p[1][1], p[1][2],
+                 p[2][0], p[2][1], p[2][2]);
+      glTexCoord2fv(t[0]); glVertex3fv(p[0]);
+      glTexCoord2fv(t[1]); glVertex3fv(p[1]);
+      glTexCoord2fv(t[2]); glVertex3fv(p[2]);
+      glTexCoord2fv(t[3]); glVertex3fv(p[3]);
+      glEnd();
+      mi->polygon_count++;
+
+      /* The triangles between the quads */
+# if 0
+      /* #### There is a fucking gap between the two black triangles 
+         that I can't figure out!  So instead of drawing the triangles,
+         we build a black shield around the middle bit in leviathan()
+         and count on back-face culling to have roughly the same effect.
+       */
+      if (!wire)
+        {
+          GLfloat pp[4][3];
+          memcpy (pp, p, sizeof(pp));
+          memcpy (pp[2], pp[1], sizeof(pp[1]));
+          pp[2][0] -= 0.5 * (1-ratio);
+          pp[2][1] -= 0.5 * (1-ratio);
+
+          glBindTexture (GL_TEXTURE_2D, 0);
+          set_colors_alpha (black_color, 1-ratio);
+
+          glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLES);
+          do_normal (pp[0][0], pp[0][1], pp[0][2],
+                     pp[2][0], pp[2][1], pp[2][2],
+                     pp[1][0], pp[1][1], pp[1][2]);
+          glVertex3fv(pp[0]);
+          glVertex3fv(pp[2]);
+          glVertex3fv(pp[1]);
+          glEnd();
+          mi->polygon_count++;
+        }
+# endif
+
+      glPopMatrix();
     }
 
-  for (i = 0; i < countof(lifter_perimiter_points_3); i++)
-    {
-      int j = (i+1 >= countof(lifter_perimiter_points_3) ? 0 : i+1);
-      GLfloat x1 = lifter_perimiter_points_3[i][0] / 255.0;
-      GLfloat y1 = lifter_perimiter_points_3[i][1] / 255.0;
-      GLfloat x2 = lifter_perimiter_points_3[j][0] / 255.0;
-      GLfloat y2 = lifter_perimiter_points_3[j][1] / 255.0;
-      int texture = -1;
-      GLfloat s1=0, t1=0, s2=0, t2=0, s3=0, t3=0, s4=0, t4=0;
-
-      if (i == 0)
-       {
-         texture = lc->texids[FACE_D];
-         s1 = 1.0;  t1 = 0.235;
-         s2 = 1.0;  t2 = 0.765;
-         s3 = 0.75; t3 = 0.765;
-         s4 = 0.75; t4 = 0.235;
-       }
+  if (! wire) glDisable (GL_BLEND);
 
-      face4((texture == -1 ? 0 : texture),
-           (texture == -1 ? interior_color : exterior_color),
-           wire,
-           s1, t1,  0.5,  x2-0.5, y2-0.5,
-           s2, t2,  0.5,  x1-0.5, y1-0.5,
-           s3, t3,  0.25, x1-0.5, y1-0.5,
-           s4, t4,  0.25, x2-0.5, y2-0.5);
-    }
+  glPopMatrix();
+}
 
-  glEndList();
 
-  /* -------------------------------------------------------------------- */
+static int
+lament_sphere (ModeInfo *mi, GLfloat ratio)
+{
+  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+  Bool wire = MI_IS_WIREFRAME(mi);
+  GLfloat size = 3; /* 3" square */
+  int polys = 0;
+  int facets = 16;  /* NxN grid on each face */
+  int face;
+  static const GLfloat norms[6][3] = {{ 0, -1, 0 }, {  0, 0, 1 }, { 0, 1, 0 },
+                                      { 0, 0, -1 }, { -1, 0, 0 }, { 1, 0, 0 }};
+  GLfloat s = 1.0 / facets;
+
+  /* The ratio used for the normals: linger on the square normals. */
+  GLfloat ratio2 = 1 - sin ((1 - ratio) / 2 * M_PI);
+  GLfloat r1 = 1 - ratio2 / 2;
+  GLfloat r2 =     ratio2 / 2;
 
-  glNewList(lc->taser_slider, GL_COMPILE);
-  glShadeModel(GL_SMOOTH);
+  glPushMatrix();
+  glTranslatef (-0.5, -0.5, -0.5);
+  glScalef (1/size, 1/size, 1/size);
 
-  /* Top face */
-  for (i = 0; i < countof(slider_face_points)/5; i++)
-    {
-      int j;
-#ifdef HAVE_GLBINDTEXTURE
-      glBindTexture(GL_TEXTURE_2D, lc->texids[FACE_E]);
-#endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
-
-      do_normal(
-          0, slider_face_points[(i*5)+0][0], slider_face_points[(i*5)+0][1],
-          0, slider_face_points[(i*5)+1][0], slider_face_points[(i*5)+1][1],
-          0, slider_face_points[(i*5)+2][0], slider_face_points[(i*5)+2][1]);
-      glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
-      for (j = 0; j < 5; j++)
-       {
-         int ix = slider_face_points[(i*5)+j][0];
-         int iy = slider_face_points[(i*5)+j][1];
-         GLfloat x, y;
-         if (ix == -1)  /* these are padding: ignore them */
-           continue;
-         x = ix / 255.0L;
-         y = iy / 255.0L;
-         glTexCoord2f(x, y);
-         glVertex3f(0.5, x-0.5, y-0.5);
-       }
-      glEnd();
-    }
+  set_colors (exterior_color);
 
-  /* Bottom face */
-  for (i = countof(slider_face_points)/5 - 1; i >= 0; i--)
+  for (face = 0; face < 6; face++)
     {
-      int j;
-#ifdef HAVE_GLBINDTEXTURE
-      glBindTexture(GL_TEXTURE_2D, 0);
-#endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, interior_color);
-
-      do_normal(
-          0, slider_face_points[(i*5)+2][0], slider_face_points[(i*5)+2][1],
-          0, slider_face_points[(i*5)+1][0], slider_face_points[(i*5)+1][1],
-          0, slider_face_points[(i*5)+0][0], slider_face_points[(i*5)+0][1]);
-      glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
-      for (j = 4; j >= 0; j--)
-       {
-         int ix = slider_face_points[(i*5)+j][0];
-         int iy = slider_face_points[(i*5)+j][1];
-         GLfloat x, y;
-         if (ix == -1)  /* these are padding: ignore them */
-           continue;
-         x = ix / 255.0L;
-         y = iy / 255.0L;
-         glTexCoord2f(x, y);
-         glVertex3f(0.25, x-0.5, y-0.5);
-       }
-      glEnd();
+      GLfloat x0, y0;
+      for (y0 = 0; y0 < 1; y0 += s)
+        for (x0 = 0; x0 < 1; x0 += s)
+        {
+          int i;
+          GLfloat x1 = x0 + s;
+          GLfloat y1 = y0 + s;
+          GLfloat pa[4][3];    /* verts of the cube */
+          GLfloat pb[4][3];    /* verts of the transition to the sphere */
+          Bool frontp;
+          GLfloat norm[4][3];  /* normals of the transitional verts */
+
+          if (norms[face][0])
+            frontp = norms[face][0] < 0,
+            pa[0][1] = x0, pa[0][2] = y0, pa[0][0] = (frontp ? 0 : 1),
+            pa[1][1] = x1, pa[1][2] = y0, pa[1][0] = pa[0][0],
+            pa[2][1] = x1, pa[2][2] = y1, pa[2][0] = pa[0][0],
+            pa[3][1] = x0, pa[3][2] = y1, pa[3][0] = pa[0][0];
+          else if (norms[face][1])
+            frontp = norms[face][1] > 0,
+            pa[0][0] = x0, pa[0][2] = y0, pa[0][1] = (frontp ? 1 : 0),
+            pa[1][0] = x1, pa[1][2] = y0, pa[1][1] = pa[0][1],
+            pa[2][0] = x1, pa[2][2] = y1, pa[2][1] = pa[0][1],
+            pa[3][0] = x0, pa[3][2] = y1, pa[3][1] = pa[0][1];
+          else /* (norms[face][2]) */
+            frontp = norms[face][2] < 0,
+            pa[0][0] = x0, pa[0][1] = y0, pa[0][2] = (frontp ? 0 : 1),
+            pa[1][0] = x1, pa[1][1] = y0, pa[1][2] = pa[0][2],
+            pa[2][0] = x1, pa[2][1] = y1, pa[2][2] = pa[0][2],
+            pa[3][0] = x0, pa[3][1] = y1, pa[3][2] = pa[0][2];
+
+          for (i = 0; i < countof(pa); i++)
+            pa[i][0] *= size, pa[i][1] *= size, pa[i][2] *= size;
+
+          /* Convert square to sphere by treating as a normalized vector */
+          for (i = 0; i < countof(pa); i++)
+            {
+              GLfloat x = (pa[i][0] / size) - 0.5;
+              GLfloat y = (pa[i][1] / size) - 0.5;
+              GLfloat z = (pa[i][2] / size) - 0.5;
+              GLfloat d = sqrt (x*x + y*y + z*z) / 2;
+              x = x/d + size/2;
+              y = y/d + size/2;
+              z = z/d + size/2;
+
+              pb[i][0] = pa[i][0] + ((x - pa[i][0]) * ratio);
+              pb[i][1] = pa[i][1] + ((y - pa[i][1]) * ratio);
+              pb[i][2] = pa[i][2] + ((z - pa[i][2]) * ratio);
+            }
+
+          /* The normals of an intermediate point are the weighted average
+             of the cube's orthogonal normals, and the sphere's radial
+             normals: early in the sequence, the edges are sharp, but they
+             soften as it expands. */
+          {
+            XYZ na, pa0, pa1, pa2;
+            pa0.x = pa[0][0]; pa0.y = pa[0][1]; pa0.z = pa[0][2];
+            pa1.x = pa[1][0]; pa1.y = pa[1][1]; pa1.z = pa[1][2];
+            pa2.x = pa[2][0]; pa2.y = pa[2][1]; pa2.z = pa[2][2];
+            na = calc_normal (pa0, pa1, pa2);
+
+            for (i = 0; i < countof(pb); i++)
+              {
+                GLfloat d;
+                XYZ nb;
+
+                nb.x = pb[i][0];
+                nb.y = pb[i][1];
+                nb.z = pb[i][2];
+                d = sqrt (nb.x*nb.x + nb.y*nb.y + nb.z*nb.z);  /* normalize */
+                nb.x /= d;
+                nb.y /= d;
+                nb.z /= d;
+
+                norm[i][0] = (na.x * r1) + (nb.x * r2);  /* weighted */
+                norm[i][1] = (na.y * r1) + (nb.y * r2);
+                norm[i][2] = (na.z * r1) + (nb.z * r2);
+              }
+          }
+
+         if (! wire)
+            glBindTexture (GL_TEXTURE_2D, lc->texids[face]);
+
+          glFrontFace (frontp ? GL_CW : GL_CCW);
+          glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+
+          texturize_vert (mi, face+1, pa[0]);
+          glNormal3fv (norm[0]);
+          glVertex3fv (pb[0]);
+
+          texturize_vert (mi, face+1, pa[1]);
+          glNormal3fv (norm[1]);
+          glVertex3fv (pb[1]);
+
+          texturize_vert (mi, face+1, pa[2]);
+          glNormal3fv (norm[2]);
+          glVertex3fv (pb[2]);
+
+          texturize_vert (mi, face+1, pa[3]);
+          glNormal3fv (norm[3]);
+          glVertex3fv (pb[3]);
+
+          glEnd();
+          polys++;
+        }
     }
 
-  /* Side walls */
-  for (i = 0; i < countof(slider_perimiter_points); i++)
-    {
-      int j = (i+1 >= countof(slider_perimiter_points) ? 0 : i+1);
-      GLfloat x1 = slider_perimiter_points[i][0] / 255.0;
-      GLfloat y1 = slider_perimiter_points[i][1] / 255.0;
-      GLfloat x2 = slider_perimiter_points[j][0] / 255.0;
-      GLfloat y2 = slider_perimiter_points[j][1] / 255.0;
-      int texture = -1;
-      GLfloat s1=0, t1=0, s2=0, t2=0, s3=0, t3=0, s4=0, t4=0;
-
-      if (i == 1)
-       {
-         texture = lc->texids[FACE_S];
-         s1 = 1.0;  t1 = 0.255;
-         s2 = 1.0;  t2 = 0.435;
-         s3 = 0.75; t3 = 0.435;
-         s4 = 0.75; t4 = 0.255;
-       }
-      else if (i == 4)
-       {
-         texture = lc->texids[FACE_D];
-         s1 = 1.0;  t1 = 0.758;
-         s2 = 1.0;  t2 = 0.915;
-         s3 = 0.75; t3 = 0.915;
-         s4 = 0.75; t4 = 0.758;
-       }
-      else if (i == 16)
-       {
-         texture = lc->texids[FACE_D];
-         s1 = 1.0;  t1 = 0.095;
-         s2 = 1.0;  t2 = 0.24;
-         s3 = 0.75; t3 = 0.24;
-         s4 = 0.75; t4 = 0.095;
-       }
-      else if (i == 19)
-       {
-         texture = lc->texids[FACE_N];
-         s1 = 1.0;  t1 = 0.568;
-         s2 = 1.0;  t2 = 0.742;
-         s3 = 0.75; t3 = 0.742;
-         s4 = 0.75; t4 = 0.568;
-       }
-
-      face4((texture == -1 ? 0 : texture),
-           (texture == -1 ? interior_color : exterior_color),
-           wire,
-           s1, t1,  0.5,  x2-0.5, y2-0.5,
-           s2, t2,  0.5,  x1-0.5, y1-0.5,
-           s3, t3,  0.25, x1-0.5, y1-0.5,
-           s4, t4,  0.25, x2-0.5, y2-0.5);
-    }
+  glPopMatrix();
 
-  glEndList();
+  return polys;
 }
 
 
-\f
-/* Rendering and animating object models
- */
-
 static void
-draw(ModeInfo *mi)
+draw (ModeInfo *mi)
 {
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
   Bool wire = MI_IS_WIREFRAME(mi);
 
+  mi->polygon_count = 0;
+
   if (!wire)
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   else
     glClear(GL_COLOR_BUFFER_BIT);
 
   glPushMatrix();
-  {
-    GLfloat x = lc->rotx;
-    GLfloat y = lc->roty;
-    GLfloat z = lc->rotz;
 
-#if 0
- x=0.75; y=0; z=0; 
-#endif
+  gltrackball_rotate (lc->trackball);
 
-    if (x < 0) x = 1 - (x + 1);
-    if (y < 0) y = 1 - (y + 1);
-    if (z < 0) z = 1 - (z + 1);
+  /* Make into the screen be +Y, right be +X, and up be +Z. */
+  glRotatef (-90.0, 1.0, 0.0, 0.0);
+
+  scale_for_window (mi);
+
+  /* Apply rotation to the object. */
+  if (lc->type != LAMENT_LID_ZOOM)
+    get_rotation (lc->rot, &lc->rotx, &lc->roty, &lc->rotz,
+                  !lc->button_down_p);
+# ifdef DEBUG_MODE
+  lc->rotx = 0.18;
+  lc->roty = 0.22;
+  lc->rotx = lc->roty = 0;
+  lc->rotz = 0;
+# endif
 
-    /* Make into the screen be +Y right be +X, and up be +Z. */
-    glRotatef(-90.0, 1.0, 0.0, 0.0);
+  glRotatef (lc->rotx * 360, 1, 0, 0);
+  glRotatef (lc->roty * 360, 0, 1, 0);
+  glRotatef (lc->rotz * 360, 0, 0, 1);
 
-    /* Scale it up. */
-    glScalef(4.0, 4.0, 4.0);
+  glScalef (0.5, 0.5, 0.5);
 
-#ifdef DEBUG
-    glPushMatrix();
+  switch (lc->type)
     {
-      /* Shift to the upper left, and draw the vanilla box. */
-      glTranslatef(-0.6, 0.0, 0.6);
+    case LAMENT_BOX:
+      glCallList (lc->dlists[OBJ_BOX]);
+      mi->polygon_count += lc->polys[OBJ_BOX];
+      break;
+
+    case LAMENT_STAR_OUT:
+    case LAMENT_STAR_ROT:
+    case LAMENT_STAR_ROT_IN:
+    case LAMENT_STAR_ROT_OUT:
+    case LAMENT_STAR_UNROT:
+    case LAMENT_STAR_IN:
+      glTranslatef (0.0, 0.0, lc->anim_z/2);
+      glRotatef (lc->anim_r/2, 0.0, 0.0, 1.0);
+      glCallList (lc->dlists[OBJ_STAR_U]);
+      mi->polygon_count += lc->polys[OBJ_STAR_U];
+
+      glTranslatef (0.0, 0.0, -lc->anim_z);
+      glRotatef (-lc->anim_r, 0.0, 0.0, 1.0);
+      glCallList (lc->dlists[OBJ_STAR_D]);
+      mi->polygon_count += lc->polys[OBJ_STAR_D];
+      break;
 
-      /* Apply rotation to the object. */
-      glRotatef(x * 360, 1.0, 0.0, 0.0);
-      glRotatef(y * 360, 0.0, 1.0, 0.0);
-      glRotatef(z * 360, 0.0, 0.0, 1.0);
+    case LAMENT_TETRA_UNE:
+    case LAMENT_TETRA_USW:
+    case LAMENT_TETRA_DWN:
+    case LAMENT_TETRA_DSE:
+      {
+        int magic;
+        GLfloat x, y, z;
+        switch (lc->type) {
+        case LAMENT_TETRA_UNE: magic = OBJ_TETRA_UNE; x= 1; y= 1; z= 1; break;
+        case LAMENT_TETRA_USW: magic = OBJ_TETRA_USW; x= 1; y= 1; z=-1; break;
+        case LAMENT_TETRA_DWN: magic = OBJ_TETRA_DWN; x= 1; y=-1; z= 1; break;
+        case LAMENT_TETRA_DSE: magic = OBJ_TETRA_DSE; x=-1; y= 1; z= 1; break;
+        default: abort(); break;
+        }
+        glCallList(lc->dlists[OBJ_TETRA_BASE]);
+        mi->polygon_count += lc->polys[OBJ_TETRA_BASE];
+        if (magic != OBJ_TETRA_UNE) glCallList (lc->dlists[OBJ_TETRA_UNE]);
+        if (magic != OBJ_TETRA_USW) glCallList (lc->dlists[OBJ_TETRA_USW]);
+        if (magic != OBJ_TETRA_DWN) glCallList (lc->dlists[OBJ_TETRA_DWN]);
+        if (magic != OBJ_TETRA_DSE) glCallList (lc->dlists[OBJ_TETRA_DSE]);
+        glRotatef (lc->anim_r, x, y, z);
+        glCallList (lc->dlists[magic]);
+        mi->polygon_count += lc->polys[magic] * 3;
+      }
+      break;
 
-      /* Draw it. */
-      glCallList(lc->box);
-    }
-    glPopMatrix();
+    case LAMENT_LID_OPEN:
+    case LAMENT_LID_CLOSE:
+    case LAMENT_LID_ZOOM:
+      {
+        GLfloat d = 0.21582;
+        int i;
+        const int lists[4] = { OBJ_LID_A, OBJ_LID_B, OBJ_LID_C, OBJ_LID_D };
+
+        lc->facing_p = facing_screen_p (mi);
+
+        if (lc->anim_z < 0.5)
+          glTranslatef (0, -30 * lc->anim_z, 0);    /* zoom */
+        else
+          glTranslatef (8 * (0.5 - (lc->anim_z - 0.5)), 0, 0);
+
+        glCallList (lc->dlists[OBJ_LID_BASE]);
+        mi->polygon_count += lc->polys[OBJ_LID_BASE];
+        for (i = 0; i < countof(lists); i++)
+          {
+            glPushMatrix();
+            glRotatef (90 * i, 0, 1, 0);
+            glTranslatef (-d, -0.5, d);
+            glRotatef (-45, 0, 1, 0);
+            glRotatef (-lc->anim_r, 1, 0, 0);
+            glRotatef (45, 0, 1, 0);
+            glTranslatef (d, 0.5, -d);
+            glRotatef (-90 * i, 0, 1, 0);
+            glCallList (lc->dlists[lists[i]]);
+            mi->polygon_count += lc->polys[lists[i]];
+            glPopMatrix();
+          }
+
+# ifdef DEBUG_MODE
+        if (lc->facing_p)
+          {
+            glColor3f(1, 0, 0);
+            glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+            glVertex3f (-0.49, 0.49, -0.49);
+            glVertex3f ( 0.49, 0.49, -0.49);
+            glVertex3f ( 0.49, 0.49,  0.49);
+            glVertex3f (-0.49, 0.49,  0.49);
+            glEnd();
+            mi->polygon_count++;
+          }
+# endif /* DEBUG_MODE */
+      }
+      break;
 
-    /* Shift to the lower right, and draw the animated object. */
-    glTranslatef(0.6, 0.0, -0.6);
-#endif /* DEBUG */
+    case LAMENT_TASER_OUT:
+    case LAMENT_TASER_SLIDE:
+    case LAMENT_TASER_SLIDE_IN:
+    case LAMENT_TASER_IN:
 
+      glTranslatef (0, -lc->anim_z/2, 0);
+      glCallList (lc->dlists[OBJ_TASER_BASE]);
+      mi->polygon_count += lc->polys[OBJ_TASER_BASE];
 
-    glPushMatrix();
-    {
-      /* Apply rotation to the object. */
-      glRotatef(x * 360, 1.0, 0.0, 0.0);
-      glRotatef(y * 360, 0.0, 1.0, 0.0);
-      glRotatef(z * 360, 0.0, 0.0, 1.0);
+      glTranslatef (0, lc->anim_z, 0);
+      glCallList (lc->dlists[OBJ_TASER_A]);
+      mi->polygon_count += lc->polys[OBJ_TASER_A];
 
-      switch (lc->type)
-       {
-       case LAMENT_BOX:
-         glCallList(lc->box);
-         break;
-
-       case LAMENT_STAR_OUT:
-       case LAMENT_STAR_ROT:
-       case LAMENT_STAR_ROT_IN:
-       case LAMENT_STAR_ROT_OUT:
-       case LAMENT_STAR_UNROT:
-       case LAMENT_STAR_IN:
-         glTranslatef(0.0, 0.0, lc->anim_z/2);
-         glRotatef(lc->anim_r/2, 0.0, 0.0, 1.0);
-         glCallList(lc->star1);
-
-         glTranslatef(0.0, 0.0, -lc->anim_z);
-         glRotatef(-lc->anim_r, 0.0, 0.0, 1.0);
-         glCallList(lc->star2);
-         break;
-
-       case LAMENT_TETRA_UNE:
-       case LAMENT_TETRA_USW:
-       case LAMENT_TETRA_DWN:
-       case LAMENT_TETRA_DSE:
-         {
-           int magic;
-           GLfloat x, y, z;
-           switch (lc->type) {
-           case LAMENT_TETRA_UNE: magic = lc->tetra_une;
-             x = 1.0; y = 1.0; z = 1.0; break;
-           case LAMENT_TETRA_USW: magic = lc->tetra_usw;
-             x = 1.0; y = 1.0; z = -1.0; break;
-           case LAMENT_TETRA_DWN: magic = lc->tetra_dwn;
-             x = 1.0; y = -1.0; z = 1.0; break;
-           case LAMENT_TETRA_DSE: magic = lc->tetra_dse;
-             x = -1.0; y = 1.0; z = 1.0; break;
-           default: abort(); break;
-           }
-           glCallList(lc->tetra_mid);
-           if (magic != lc->tetra_une) glCallList(lc->tetra_une);
-           if (magic != lc->tetra_usw) glCallList(lc->tetra_usw);
-           if (magic != lc->tetra_dwn) glCallList(lc->tetra_dwn);
-           if (magic != lc->tetra_dse) glCallList(lc->tetra_dse);
-           glRotatef(lc->anim_r, x, y, z);
-           glCallList(magic);
-         }
-         break;
-
-       case LAMENT_LID_OPEN:
-       case LAMENT_LID_CLOSE:
-       case LAMENT_LID_ZOOM:
-         {
-           GLfloat d = 0.417;
-
-           glTranslatef(lc->anim_z, 0.0, 0.0);
-
-           glCallList(lc->lid_0);
-
-           glPushMatrix();
-             glTranslatef(-0.5, -d, 0.0);
-             glRotatef(-lc->anim_r, 0.0, -1.0, -1.0);
-             glTranslatef( 0.5,  d, 0.0);
-             glCallList(lc->lid_1);
-           glPopMatrix();
-           glPushMatrix();
-             glTranslatef(-0.5, -d, 0.0);
-             glRotatef( lc->anim_r, 0.0, -1.0,   1.0);
-             glTranslatef( 0.5,  d, 0.0);
-             glCallList(lc->lid_2);
-           glPopMatrix();
-           glPushMatrix();
-             glTranslatef(-0.5,  d, 0.0);
-             glRotatef( lc->anim_r, 0.0,  -1.0,  -1.0);
-             glTranslatef( 0.5, -d, 0.0);
-             glCallList(lc->lid_3);
-           glPopMatrix();
-           glPushMatrix();
-             glTranslatef(-0.5,  d, 0.0);
-             glRotatef(-lc->anim_r, 0.0,  -1.0,   1.0);
-             glTranslatef( 0.5, -d, 0.0);
-             glCallList(lc->lid_4);
-           glPopMatrix();
-         }
-         break;
-
-       case LAMENT_TASER_OUT:
-       case LAMENT_TASER_SLIDE:
-       case LAMENT_TASER_SLIDE_IN:
-       case LAMENT_TASER_IN:
-
-         glTranslatef(-lc->anim_z/2, 0.0, 0.0);
-         glCallList(lc->taser_base);
-
-         glTranslatef(lc->anim_z, 0.0, 0.0);
-         glCallList(lc->taser_lifter);
-
-         glTranslatef(0.0, 0.0, lc->anim_y);
-         glCallList(lc->taser_slider);
-         break;
-
-       default:
-         abort();
-         break;
-       }
+      glTranslatef (lc->anim_y, 0, 0);
+      glCallList (lc->dlists[OBJ_TASER_B]);
+      mi->polygon_count += lc->polys[OBJ_TASER_B];
+      break;
+
+    case LAMENT_PILLAR_OUT:
+    case LAMENT_PILLAR_SPIN:
+    case LAMENT_PILLAR_IN:
+
+      glCallList (lc->dlists[OBJ_PILLAR_BASE]);
+      mi->polygon_count += lc->polys[OBJ_PILLAR_BASE];
+
+      glPushMatrix();
+      if (lc->anim_z == 1 || lc->anim_z == 3)
+        {
+          glRotatef (lc->anim_r, 0, 0, 1);
+          glTranslatef (0, 0, lc->anim_y);
+        }
+      glCallList (lc->dlists[OBJ_PILLAR_A]);
+      mi->polygon_count += lc->polys[OBJ_PILLAR_A];
+      glPopMatrix();
+
+      glPushMatrix();
+      if (lc->anim_z == 2 || lc->anim_z == 3)
+        {
+          glRotatef (lc->anim_r, 0, 0, 1);
+          glTranslatef (0, 0, -lc->anim_y);
+        }
+      glCallList (lc->dlists[OBJ_PILLAR_B]);
+      mi->polygon_count += lc->polys[OBJ_PILLAR_B];
+      glPopMatrix();
+      break;
+
+    case LAMENT_SPHERE_OUT:
+    case LAMENT_SPHERE_IN:
+      mi->polygon_count += lament_sphere (mi, lc->anim_y);
+      break;
+
+    case LAMENT_LEVIATHAN_SPIN:
+    case LAMENT_LEVIATHAN_UNSPIN:
+    case LAMENT_LEVIATHAN_FADE:
+    case LAMENT_LEVIATHAN_UNFADE:
+    case LAMENT_LEVIATHAN_TWIST:
+    case LAMENT_LEVIATHAN_UNTWIST:
+      {
+        /* These normals are hard to compute, so I pulled them from the
+           model. */
+        const GLfloat axes[6][4] =
+          {{ OBJ_ISO_UNE,  0.633994, 0.442836,   0.633994 },
+           { OBJ_ISO_USW,  0.442836,  0.633994, -0.633994 },
+           { OBJ_ISO_DSE, -0.633994,  0.633994,  0.442836 },
+           { OBJ_ISO_SWD, -0.633994, -0.442836, -0.633994 },
+           { OBJ_ISO_DEN, -0.442836, -0.633994,  0.633994 },
+           { OBJ_ISO_UNW,  0.633994, -0.633994, -0.442836 }};
+        int i;
+
+        GLfloat s = (1 - lc->anim_z);
+        GLfloat s2 = MAX (0, 360 - lc->anim_r) / 360.0;
+        Bool blendp = 0;
+
+        switch (lc->type) {
+        case LAMENT_LEVIATHAN_SPIN: break;
+        case LAMENT_LEVIATHAN_UNSPIN: s2 = 1 - s2; break;
+        default: s2 = 0; blendp = 1; break;
+        }
+
+        if (wire) blendp = 0;
+
+        s  = (s * 0.6) + 0.4;
+
+        leviathan (mi, 1 - s2, 1, True);
+        glCallList (lc->dlists[OBJ_ISO_BASE_A]);
+        mi->polygon_count += lc->polys[OBJ_ISO_BASE_A];
+
+        glPushMatrix();
+        glScalef (s2, s2, s2);
+        glCallList (lc->dlists[OBJ_ISO_USE]);
+        mi->polygon_count += lc->polys[OBJ_ISO_USE];
+        glPopMatrix();
+
+        glPushMatrix();
+        glRotatef (lc->anim_y, 1, -1, 1);
+        glCallList (lc->dlists[OBJ_ISO_BASE_B]);
+        mi->polygon_count += lc->polys[OBJ_ISO_BASE_B];
+        leviathan (mi, 1 - s2, 1, False);
+        glPopMatrix();
+
+        if (blendp)
+          {
+# ifndef HAVE_JWZGLES /* no glBlendColor */
+            glEnable (GL_BLEND);
+            glBlendFunc (GL_CONSTANT_ALPHA, GL_SRC_ALPHA);
+            glBlendColor (1, 1, 1, MAX(0, 1-(lc->anim_z * 3)));
+# endif
+          }
+
+        for (i = 0; i < countof(axes); i++)
+          {
+            glPushMatrix();
+            glRotatef (lc->anim_r, axes[i][1], axes[i][2], axes[i][3]);
+            glScalef (s, s, s);
+            glCallList (lc->dlists[(int) axes[i][0]]);
+            mi->polygon_count += lc->polys[(int) axes[i][0]];
+            glPopMatrix();
+            if (i == 2)
+              glRotatef (lc->anim_y, 1, -1, 1);
+          }
+
+        if (blendp) glDisable (GL_BLEND);
+
+        glPushMatrix();
+        glScalef (s2, s2, s2);
+        glCallList (lc->dlists[OBJ_ISO_DWN]);
+        mi->polygon_count += lc->polys[OBJ_ISO_DWN];
+        glPopMatrix();
+      }
+      break;
+
+    case LAMENT_LEVIATHAN_COLLAPSE:
+    case LAMENT_LEVIATHAN_EXPAND:
+      {
+        glPushMatrix();
+        leviathan (mi, 1, lc->anim_y, True);
+        glRotatef (180, 1, -1, 1);
+        leviathan (mi, 1, lc->anim_y, False);
+        glPopMatrix();
+        folding_walls (mi, lc->anim_y, True);
+        folding_walls (mi, lc->anim_y, False);
+      }
+      break;
+
+    default:
+      abort();
+      break;
     }
-    glPopMatrix();
 
-  }
   glPopMatrix();
 }
 
 
+/* Rather than just picking states randomly, pick an ordering randomly, do it,
+   and then re-randomize.  That way one can be assured of seeing all states in
+   a short time period, though not always in the same order (it's frustrating
+   to see it pick the same state 5x in a row.)  Though, that can still happen,
+   since states are in the list multiple times as a way of giving them
+   probabilities.
+ */
+static void
+shuffle_states (lament_configuration *lc)
+{
+  int i;
+  for (i = 0; i < lc->nstates; i++)
+    {
+      int a = random() % lc->nstates;
+      lament_type swap = lc->states[a];
+      lc->states[a] = lc->states[i];
+      lc->states[i] = swap;
+    }
+}
+
+
 static void
-animate(ModeInfo *mi)
+animate (ModeInfo *mi)
 {
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
   int pause = 10;
-/*  int pause2 = 60;*/
-  int pause3 = 120;
+  int pause2 = 120;
+  GLfloat speed = (lc->ffwdp ? 20 : 1);
 
   switch (lc->type)
     {
     case LAMENT_BOX:
       {
-       /* Rather than just picking states randomly, pick an ordering randomly,
-          do it, and then re-randomize.  That way one can be assured of seeing
-          all states in a short time period, though not always in the same
-          order (it's frustrating to see it pick the same state 5x in a row.)
-        */
-       static lament_type states[] = {
-         LAMENT_STAR_OUT, LAMENT_STAR_OUT,
-         LAMENT_TETRA_UNE, LAMENT_TETRA_USW,
-         LAMENT_TETRA_DWN, LAMENT_TETRA_DSE,
-         LAMENT_LID_OPEN, LAMENT_LID_OPEN, LAMENT_LID_OPEN,
-         LAMENT_TASER_OUT, LAMENT_TASER_OUT,
-         LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX,
-         LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX,
-       };
-       static int state = countof(states);
-
-       if (state < countof(states))
-         {
-           lc->type = states[state++];
-         }
-       else
-         {
-           int i;
-           state = 0;
-           for (i = 0; i < countof(states); i++)
-             {
-               int a = random() % countof(states);
-               lament_type swap = states[a];
-               states[a] = states[i];
-               states[i] = swap;
-             }
-         }
+        lc->state++;
+        if (lc->state >= lc->nstates)
+          {
+            shuffle_states (lc);
+            lc->state = 0;
+          }
+        lc->type = lc->states[lc->state];
 
        if (lc->type == LAMENT_BOX)
-         lc->anim_pause = pause3;
+         lc->anim_pause = pause2;
 
        lc->anim_r = 0.0;
        lc->anim_y = 0.0;
@@ -1614,7 +1165,7 @@ animate(ModeInfo *mi)
       /* -------------------------------------------------------------- */
 
     case LAMENT_STAR_OUT:
-      lc->anim_z += 0.01;
+      lc->anim_z += 0.01 * speed;
       if (lc->anim_z >= 1.0)
        {
          lc->anim_z = 1.0;
@@ -1624,7 +1175,7 @@ animate(ModeInfo *mi)
       break;
 
     case LAMENT_STAR_ROT:
-      lc->anim_r += 1.0;
+      lc->anim_r += 1.0 * speed;
       if (lc->anim_r >= 45.0)
        {
          lc->anim_r = 45.0;
@@ -1634,17 +1185,17 @@ animate(ModeInfo *mi)
       break;
 
     case LAMENT_STAR_ROT_IN:
-      lc->anim_z -= 0.01;
+      lc->anim_z -= 0.01 * speed;
       if (lc->anim_z <= 0.0)
        {
          lc->anim_z = 0.0;
          lc->type = LAMENT_STAR_ROT_OUT;
-         lc->anim_pause = pause3 * (1 + (random() % 4) + (random() % 4));
+         lc->anim_pause = pause2 * (1 + frand(2) + frand(2));
        }
       break;
 
     case LAMENT_STAR_ROT_OUT:
-      lc->anim_z += 0.01;
+      lc->anim_z += 0.01 * speed;
       if (lc->anim_z >= 1.0)
        {
          lc->anim_z = 1.0;
@@ -1654,7 +1205,7 @@ animate(ModeInfo *mi)
       break;
       
     case LAMENT_STAR_UNROT:
-      lc->anim_r -= 1.0;
+      lc->anim_r -= 1.0 * speed;
       if (lc->anim_r <= 0.0)
        {
          lc->anim_r = 0.0;
@@ -1664,12 +1215,12 @@ animate(ModeInfo *mi)
       break;
 
     case LAMENT_STAR_IN:
-      lc->anim_z -= 0.01;
+      lc->anim_z -= 0.01 * speed;
       if (lc->anim_z <= 0.0)
        {
          lc->anim_z = 0.0;
          lc->type = LAMENT_BOX;
-         lc->anim_pause = pause3;
+         lc->anim_pause = pause2;
        }
       break;
 
@@ -1680,12 +1231,12 @@ animate(ModeInfo *mi)
     case LAMENT_TETRA_DWN:
     case LAMENT_TETRA_DSE:
 
-      lc->anim_r += 1.0;
+      lc->anim_r += 1.0 * speed;
       if (lc->anim_r >= 360.0)
        {
          lc->anim_r = 0.0;
          lc->type = LAMENT_BOX;
-         lc->anim_pause = pause3;
+         lc->anim_pause = pause2;
        }
       else if (lc->anim_r > 119.0 && lc->anim_r <= 120.0)
        {
@@ -1702,53 +1253,33 @@ animate(ModeInfo *mi)
       /* -------------------------------------------------------------- */
 
     case LAMENT_LID_OPEN:
-      lc->anim_r += 1.0;
+      lc->anim_r += 1.0 * speed;
 
       if (lc->anim_r >= 112.0)
        {
-         GLfloat hysteresis = 0.05;
-
          lc->anim_r = 112.0;
          lc->anim_z = 0.0;
-         lc->anim_pause = pause3;
-
-         if (lc->rotx >= -hysteresis &&
-             lc->rotx <=  hysteresis &&
-             ((lc->rotz >=  (0.25 - hysteresis) &&
-               lc->rotz <=  (0.25 + hysteresis)) ||
-              (lc->rotz >= (-0.25 - hysteresis) &&
-               lc->rotz <= (-0.25 + hysteresis))))
-           {
-             lc->type = LAMENT_LID_ZOOM;
-             lc->rotx = 0.00;
-             lc->rotz = (lc->rotz < 0 ? -0.25 : 0.25);
-           }
-         else
-           {
-             lc->type = LAMENT_LID_CLOSE;
-           }
-       }
+         lc->anim_pause = pause2;
+          lc->type = (lc->facing_p ? LAMENT_LID_ZOOM : LAMENT_LID_CLOSE);
+        }
       break;
 
     case LAMENT_LID_CLOSE:
-      lc->anim_r -= 1.0;
+      lc->anim_r -= 1.0 * speed;
       if (lc->anim_r <= 0.0)
        {
          lc->anim_r = 0.0;
          lc->type = LAMENT_BOX;
-         lc->anim_pause = pause3;
+         lc->anim_pause = pause2;
        }
       break;
 
     case LAMENT_LID_ZOOM:
-      lc->anim_z -= 0.1;
-      if (lc->anim_z < -50.0)
+      lc->anim_z += 0.01 * speed;
+      if (lc->anim_z > 1.0)
        {
          lc->anim_r = 0.0;
          lc->anim_z = 0.0;
-         lc->rotx = frand(1.0) * RANDSIGN();
-         lc->roty = frand(1.0) * RANDSIGN();
-         lc->rotz = frand(1.0) * RANDSIGN();
          lc->type = LAMENT_BOX;
        }
       break;
@@ -1756,27 +1287,27 @@ animate(ModeInfo *mi)
       /* -------------------------------------------------------------- */
 
     case LAMENT_TASER_OUT:
-      lc->anim_z += 0.0025;
-      if (lc->anim_z >= 0.25)
+      lc->anim_z += 0.005 * speed;
+      if (lc->anim_z >= 0.5)
        {
-         lc->anim_z = 0.25;
+         lc->anim_z = 0.5;
          lc->type = LAMENT_TASER_SLIDE;
-         lc->anim_pause = pause * (1 + (random() % 5) + (random() % 5));
+         lc->anim_pause = pause * (1 + frand(5) + frand(5));
        }
       break;
 
     case LAMENT_TASER_SLIDE:
-      lc->anim_y += 0.0025;
-      if (lc->anim_y >= 0.23)
+      lc->anim_y += 0.005 * speed;
+      if (lc->anim_y >= 0.255)
        {
-         lc->anim_y = 0.23;
+         lc->anim_y = 0.255;
          lc->type = LAMENT_TASER_SLIDE_IN;
-         lc->anim_pause = pause3 * (1 + (random() % 5) + (random() % 5));
+         lc->anim_pause = pause2 * (1 + frand(5) + frand(5));
        }
       break;
 
     case LAMENT_TASER_SLIDE_IN:
-      lc->anim_y -= 0.0025;
+      lc->anim_y -= 0.0025 * speed;
       if (lc->anim_y <= 0.0)
        {
          lc->anim_y = 0.0;
@@ -1786,172 +1317,249 @@ animate(ModeInfo *mi)
       break;
 
     case LAMENT_TASER_IN:
-      lc->anim_z -= 0.0025;
+      lc->anim_z -= 0.0025 * speed;
       if (lc->anim_z <= 0.0)
        {
          lc->anim_z = 0.0;
          lc->type = LAMENT_BOX;
-         lc->anim_pause = pause3;
+         lc->anim_pause = pause2;
        }
       break;
 
-    default:
-      abort();
+      /* -------------------------------------------------------------- */
+
+    case LAMENT_PILLAR_OUT:
+
+      if (lc->anim_y == 0)  /* mostly in */
+        lc->anim_y += 0.005 * ((random() % 5) ? -1 : 1) * speed;
+      else if (lc->anim_y > 0)
+        lc->anim_y += 0.005 * speed;
+      else
+        lc->anim_y -= 0.001 * speed;
+
+      if (lc->anim_z == 0)
+        {
+          int i = (random() % 7);   /* A, B or both */
+          if      (i == 0) lc->anim_z = 3;
+          else if (i < 5)  lc->anim_z = 2;
+          else             lc->anim_z = 1;
+
+          /* We can do quarter turns, because it's radially symmetrical. */
+          lc->anim_r =  90.0 * (1 + frand(6)) * RANDSIGN();
+        }
+      if (lc->anim_y > 0.4)
+        {
+          lc->anim_y = 0.4;
+          lc->type = LAMENT_PILLAR_SPIN;
+          lc->anim_pause = pause;
+        }
+      else if (lc->anim_y < -0.03)
+        {
+          lc->anim_y = -0.03;
+          lc->type = LAMENT_PILLAR_SPIN;
+          lc->anim_pause = pause;
+        }
       break;
-    }
-}
 
+    case LAMENT_PILLAR_SPIN:
+      {
+        Bool negp = (lc->anim_r < 0);
+        lc->anim_r += (negp ? 1 : -1) * speed;
+        if (negp ? lc->anim_r > 0 : lc->anim_r < 0)
+          {
+            lc->anim_r = 0;
+            lc->type = LAMENT_PILLAR_IN;
+          }
+      }
+      break;
 
-static void
-rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
-{
-  double ppos = *pos;
+    case LAMENT_PILLAR_IN:
+      {
+        Bool negp = (lc->anim_y < 0);
+        lc->anim_y += (negp ? 1 : -1) * 0.005 * speed;
+        if (negp ? lc->anim_y > 0 : lc->anim_y < 0)
+          {
+            lc->anim_y = 0;
+            lc->anim_z = 0;
+            lc->type = LAMENT_BOX;
+            lc->anim_pause = pause;
+          }
+      }
+      break;
 
-  /* tick position */
-  if (ppos < 0)
-    ppos = -(ppos + *v);
-  else
-    ppos += *v;
+      /* -------------------------------------------------------------- */
 
-  if (ppos > 1.0)
-    ppos -= 1.0;
-  else if (ppos < 0)
-    ppos += 1.0;
+    case LAMENT_SPHERE_OUT:
+      {
+        lc->anim_y += 0.01 * speed;
+        if (lc->anim_y >= 1)
+          {
+            lc->anim_y = 1;
+            lc->type = LAMENT_SPHERE_IN;
+            lc->anim_pause = pause2 * (1 + frand(1) + frand(1));
+          }
+      }
+      break;
 
-  if (ppos < 0) abort();
-  if (ppos > 1.0) abort();
-  *pos = (*pos > 0 ? ppos : -ppos);
+    case LAMENT_SPHERE_IN:
+      {
+        lc->anim_y -= 0.01 * speed;
+        if (lc->anim_y <= 0)
+          {
+            lc->anim_y = 0;
+            lc->type = LAMENT_BOX;
+            lc->anim_pause = pause;
+          }
+      }
+      break;
 
-  /* accelerate */
-  *v += *dv;
+      /* -------------------------------------------------------------- */
 
-  /* clamp velocity */
-  if (*v > max_v || *v < -max_v)
-    {
-      *dv = -*dv;
-    }
-  /* If it stops, start it going in the other direction. */
-  else if (*v < 0)
-    {
-      if (random() % 4)
+    case LAMENT_LEVIATHAN_SPIN:
+      lc->anim_r += 3.5 * speed;
+      if (lc->anim_r >= 360 * 3)
        {
-         *v = 0;
+         lc->anim_r = 0;
+         lc->type = LAMENT_LEVIATHAN_FADE;
+         lc->anim_pause = 0;
+       }
+      break;
 
-         /* keep going in the same direction */
-         if (random() % 2)
-           *dv = 0;
-         else if (*dv < 0)
-           *dv = -*dv;
+    case LAMENT_LEVIATHAN_FADE:
+      lc->anim_z += 0.01 * speed;
+      if (lc->anim_z >= 1)
+       {
+         lc->anim_z = 1;
+         lc->type = LAMENT_LEVIATHAN_TWIST;
+         lc->anim_pause = 0;
        }
-      else
+      break;
+
+    case LAMENT_LEVIATHAN_TWIST:
+      lc->anim_y += 2 * speed;
+      lc->anim_z = 1;
+      if (lc->anim_y >= 180)
        {
-         /* reverse gears */
-         *v = -*v;
-         *dv = -*dv;
-         *pos = -*pos;
+         lc->anim_y = 0;
+         lc->type = LAMENT_LEVIATHAN_COLLAPSE;
+         lc->anim_pause = 0;
        }
-    }
+      break;
 
-  /* Alter direction of rotational acceleration randomly. */
-  if (! (random() % 120))
-    *dv = -*dv;
+    case LAMENT_LEVIATHAN_COLLAPSE:
+      lc->anim_y += 0.01 * speed;
+      if (lc->anim_y >= 1)
+       {
+         lc->anim_y = 1.0;
+         lc->type = LAMENT_LEVIATHAN_EXPAND;
+         lc->anim_pause = pause2 * 4;
+       }
+      break;
 
-  /* Change acceleration very occasionally. */
-  if (! (random() % 200))
-    {
-      if (*dv == 0)
-       *dv = 0.00001;
-      else if (random() & 1)
-       *dv *= 1.2;
-      else
-       *dv *= 0.8;
-    }
-}
+    case LAMENT_LEVIATHAN_EXPAND:
+      lc->anim_y -= 0.005 * speed;
+      if (lc->anim_y <= 0)
+       {
+         lc->anim_y = 180;
+         lc->type = LAMENT_LEVIATHAN_UNTWIST;
+       }
+      break;
 
+    case LAMENT_LEVIATHAN_UNTWIST:
+      lc->anim_y -= 2 * speed;
+      lc->anim_z = 1;
+      if (lc->anim_y <= 0)
+       {
+         lc->anim_y = 0;
+         lc->type = LAMENT_LEVIATHAN_UNFADE;
+         lc->anim_pause = 0;
+       }
+      break;
 
-\f
-/* Window management, etc
- */
+    case LAMENT_LEVIATHAN_UNFADE:
+      lc->anim_z -= 0.1 * speed;
+      if (lc->anim_z <= 0)
+       {
+         lc->anim_z = 0;
+         lc->type = LAMENT_LEVIATHAN_UNSPIN;
+         lc->anim_pause = 0;
+       }
+      break;
 
-static void
-reshape(int width, int height)
-{
-  int target_size = 180;
-  int win_size = (width > height ? height : width);
-  GLfloat h = (GLfloat) height / (GLfloat) width;
+    case LAMENT_LEVIATHAN_UNSPIN:
+      lc->anim_r += 3.5 * speed;
+      if (lc->anim_r >= 360 * 2)
+       {
+         lc->anim_r = 0;
+         lc->type = LAMENT_BOX;
+         lc->anim_pause = pause2;
+       }
+      break;
 
-  glViewport(0, 0, (GLint) width, (GLint) height);
+    default:
+      abort();
+      break;
+    }
 
-/*  glViewport(-600, -600, 1800, 1800); */
+# ifdef DEBUG_MODE
 
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-  glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
-  glTranslatef(0.0, 0.0, -40.0);
+  lc->anim_pause = 0;
 
-  /* This scale makes the box take up most of the window */
-  glScalef(2.0, 2.0, 2.0);
+  if (lc->type == LAMENT_BOX)
+    lc->type = DEBUG_MODE;
 
-  /* But if the window is more than a little larger than our target size,
-     scale the object back down, so that the bits drawn on the screen end
-     up rougly target_size across (actually it ends up a little larger.)
-     Note that the image-map bits we have are 128x128.  Therefore, if the
-     image is magnified a lot, it looks pretty blocky.  So it's better to
-     have a 128x128 animation on a 1280x1024 screen that looks good, than
-     a 1024x1024 animation that looks really pixellated.
-   */
-  if (win_size > target_size * 1.5)
+  if (lc->ffwdp)
     {
-      GLfloat ratio = ((GLfloat) target_size / (GLfloat) win_size);
-      ratio *= 2.0;
-      glScalef(ratio, ratio, ratio);
+      lc->ffwdp = 0;
+      while (lc->type != DEBUG_MODE)
+        animate (mi);
     }
 
-  /* The depth buffer will be cleared, if needed, before the
-   * next frame.  Right now we just want to black the screen.
-   */
-  glClear(GL_COLOR_BUFFER_BIT);
+# else /* !DEBUG_MODE */
+
+  if (lc->ffwdp && lc->type == LAMENT_BOX)
+    {
+      lc->ffwdp = 0;
+      while (lc->type == LAMENT_BOX)
+        animate (mi);
+      lc->anim_pause = 0;
+    }
+
+# endif /* !DEBUG_MODE */
 }
 
 
 static void
-gl_init(ModeInfo *mi)
+gl_init (ModeInfo *mi)
 {
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
   Bool wire = MI_IS_WIREFRAME(mi);
+  int i;
 
   if (wire)
     do_texture = False;
 
   if (!wire)
     {
-      static GLfloat pos0[]  = { -4.0, 2.0, 5.0, 1.0 };
-      static GLfloat pos1[]  = { 12.0, 5.0, 1.0, 1.0 };
-      static GLfloat local[] = { 0.0 };
-      static GLfloat ambient[] = { 0.3, 0.3, 0.3, 1.0 };
-      static GLfloat spec[] = { 1.0, 1.0, 1.0, 1.0 };
-      static GLfloat shine[] = { 100.0 };
+      static const GLfloat pos0[]  = { -4.0,  2.0, 5.0, 1.0 };
+      static const GLfloat pos1[]  = {  6.0, -1.0, 3.0, 1.0 };
+
+      static const GLfloat amb0[]  = { 0.7, 0.7, 0.7, 1.0 };
+/*    static const GLfloat amb1[]  = { 0.7, 0.0, 0.0, 1.0 }; */
+      static const GLfloat dif0[]  = { 1.0, 1.0, 1.0, 1.0 };
+      static const GLfloat dif1[]  = { 0.3, 0.1, 0.1, 1.0 };
 
       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
 
-      glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
-      glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
-
-      glLightfv(GL_LIGHT0, GL_SPECULAR, spec);
-      glLightfv(GL_LIGHT1, GL_SPECULAR, spec);
-
-      glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local);
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
-      glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
-      glMaterialfv(GL_FRONT, GL_SHININESS, shine);
+      glLightfv(GL_LIGHT0, GL_AMBIENT,  amb0);
+/*    glLightfv(GL_LIGHT1, GL_AMBIENT,  amb1); */
+      glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif0);
+      glLightfv(GL_LIGHT1, GL_DIFFUSE,  dif1);
 
       glEnable(GL_LIGHTING);
       glEnable(GL_LIGHT0);
-      glEnable(GL_LIGHT1);
-      glDisable(GL_LIGHT1);
+/*    glEnable(GL_LIGHT1); */
 
       glEnable(GL_DEPTH_TEST);
       glEnable(GL_TEXTURE_2D);
@@ -1961,121 +1569,196 @@ gl_init(ModeInfo *mi)
 
   if (do_texture)
     {
-#ifdef HAVE_GLBINDTEXTURE
       int i;
-      for (i = 0; i < 6; i++)
+      for (i = 0; i < countof(lc->texids); i++)
        glGenTextures(1, &lc->texids[i]);
 
-      parse_image_data(mi);
+      lc->texture = xpm_to_ximage (mi->dpy,
+                                   mi->xgwa.visual,
+                                   mi->xgwa.colormap,
+                                   lament512);
 
       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-      glPixelStorei(GL_UNPACK_ROW_LENGTH, lc->texture->width);
+      /* messes up -fps */
+      /* glPixelStorei(GL_UNPACK_ROW_LENGTH, lc->texture->width); */
 
-      for (i = 0; i < 6; i++)
+      for (i = 0; i < countof(lc->texids); i++)
        {
          int height = lc->texture->width;      /* assume square */
          glBindTexture(GL_TEXTURE_2D, lc->texids[i]);
-         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
+
+          clear_gl_error();
          glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                       lc->texture->width, height, 0,
-                      GL_RGBA, GL_UNSIGNED_BYTE,
+                      GL_RGBA,
+                       /* GL_UNSIGNED_BYTE, */
+                       GL_UNSIGNED_INT_8_8_8_8_REV,
                       (lc->texture->data +
                        (lc->texture->bytes_per_line * height * i)));
-
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
-         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+          check_gl_error("texture");
+
+          glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+          glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+          glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+          glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+          check_gl_error("texture");
+
+          /* This makes scaled pixmaps tolerable to look at. */
+# if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
+#   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
+# endif
+# ifdef GL_TEXTURE_LOD_BIAS
+          glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
+# endif
+          clear_gl_error();  /* invalid enum on iPad 3 */
        }
-
-#else  /* !HAVE_GLBINDTEXTURE */
-      fprintf(stderr,
-             "%s: this version of GL doesn't support multiple texture maps.\n"
-             "\tGet OpenGL 1.1.\n",
-             progname);
-      exit (1);
-#endif /* !HAVE_GLBINDTEXTURE */
     }
 
-  lc->box = glGenLists(16);
-  lc->star1 = lc->box+1;
-  lc->star2 = lc->box+2;
-  lc->tetra_une = lc->box+3;
-  lc->tetra_usw = lc->box+4;
-  lc->tetra_dwn = lc->box+5;
-  lc->tetra_dse = lc->box+6;
-  lc->tetra_mid = lc->box+7;
-  lc->lid_0 = lc->box+8;
-  lc->lid_1 = lc->box+9;
-  lc->lid_2 = lc->box+10;
-  lc->lid_3 = lc->box+11;
-  lc->lid_4 = lc->box+12;
-  lc->taser_base = lc->box+13;
-  lc->taser_lifter = lc->box+14;
-  lc->taser_slider = lc->box+15;
-
-  box(mi, wire);
-  star(mi, True, wire);
-  star(mi, False, wire);
-  tetra(mi, wire);
-  lid(mi, wire);
-  taser(mi, wire);
+  for (i = 0; i < countof(all_objs); i++)
+    {
+      GLfloat s = 1/3.0;  /* box is 3" square */
+      const struct gllist *L = *all_objs[i];
+      const GLfloat *f = (const GLfloat *) L->data;
+      int j;
+
+      lc->dlists[i] = glGenLists(1);
+      lc->polys[i] = L->points / 3;
+      glNewList(lc->dlists[i], GL_COMPILE);
+      if (L->primitive != GL_TRIANGLES) abort();
+      if (L->format != GL_N3F_V3F) abort();
+
+      glPushMatrix();
+      glTranslatef (-0.5, -0.5, -0.5);
+      glScalef (s, s, s);
+      
+      for (j = 0; j < L->points; j += 3)
+        {
+          int face, outerp;
+          Bool blackp = (i == OBJ_ISO_BASE_A || i == OBJ_ISO_BASE_B);
+          which_face (mi, f, &face, &outerp); /* from norm of first vert */
+
+          set_colors (outerp ? exterior_color :
+                      blackp ? black_color : interior_color);
+          glBindTexture (GL_TEXTURE_2D, 
+                         (outerp ? lc->texids[face-1] :
+                          blackp ? 0 : lc->texids[6]));
+
+          glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
+          if (face) texturize_vert (mi, face, f+3);
+          glNormal3fv (f); f += 3; glVertex3fv (f); f += 3;
+          if (face) texturize_vert (mi, face, f+3);
+          glNormal3fv (f); f += 3; glVertex3fv (f); f += 3;
+          if (face) texturize_vert (mi, face, f+3);
+          glNormal3fv (f); f += 3; glVertex3fv (f); f += 3;
+          glEnd();
+        }
+      glPopMatrix();
+
+      glEndList();
+    }
 }
 
 
-void
-init_lament(ModeInfo *mi)
+ENTRYPOINT Bool
+lament_handle_event (ModeInfo *mi, XEvent *event)
 {
-  lament_configuration *lc;
-  if (!lcs)
+  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+
+  if (gltrackball_event_handler (event, lc->trackball,
+                                 MI_WIDTH (mi), MI_HEIGHT (mi),
+                                 &lc->button_down_p))
+    return True;
+  else if (event->xany.type == KeyPress)
     {
-      lcs = (lament_configuration *)
-       calloc(MI_NUM_SCREENS(mi), sizeof (lament_configuration));
-      if (!lcs)
-       {
-         fprintf(stderr, "%s: out of memory\n", progname);
-         exit(1);
-       }
+      KeySym keysym;
+      char c = 0;
+      XLookupString (&event->xkey, &c, 1, &keysym, 0);
+      if (c == ' ' || c == '\t')
+        {
+          lc->ffwdp = True;
+          return True;
+        }
     }
 
-  lc = &lcs[MI_SCREEN(mi)];
+  return False;
+}
 
-  lc->rotx = frand(1.0) * RANDSIGN();
-  lc->roty = frand(1.0) * RANDSIGN();
-  lc->rotz = frand(1.0) * RANDSIGN();
 
-  /* bell curve from 0-1.5 degrees, avg 0.75 */
-  lc->dx = (frand(1) + frand(1) + frand(1)) / (360*2);
-  lc->dy = (frand(1) + frand(1) + frand(1)) / (360*2);
-  lc->dz = (frand(1) + frand(1) + frand(1)) / (360*2);
+ENTRYPOINT void
+reshape_lament (ModeInfo *mi, int width, int height)
+{
+  GLfloat h = (GLfloat) height / (GLfloat) width;
+  glViewport(0, 0, (GLint) width, (GLint) height);
 
-  lc->d_max = lc->dx * 2;
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  glTranslatef(0.0, 0.0, -40.0);
+  glClear(GL_COLOR_BUFFER_BIT);
+}
 
-  lc->ddx = 0.00006 + frand(0.00003);
-  lc->ddy = 0.00006 + frand(0.00003);
-  lc->ddz = 0.00006 + frand(0.00003);
 
-  lc->ddx = 0.00001;
-  lc->ddy = 0.00001;
-  lc->ddz = 0.00001;
+ENTRYPOINT void
+init_lament (ModeInfo *mi)
+{
+  lament_configuration *lc;
+  int i;
+  MI_INIT (mi, lcs, NULL);
+
+  lc = &lcs[MI_SCREEN(mi)];
+
+  {
+    double rot_speed = 0.5;
+    lc->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
+    lc->trackball = gltrackball_init (True);
+  }
 
   lc->type = LAMENT_BOX;
   lc->anim_pause = 300 + (random() % 100);
 
   if ((lc->glx_context = init_GL(mi)) != NULL)
     {
-      reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
+      reshape_lament(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
       gl_init(mi);
     }
+
+  lc->states = (lament_type *) calloc (200, sizeof (*lc->states));
+  lc->nstates = 0;
+
+# define PUSH(N,WHICH) \
+    for (i = 0; i < N; i++) lc->states[lc->nstates++] = WHICH
+
+  PUSH (4, LAMENT_TETRA_UNE);          /* most common */
+  PUSH (4, LAMENT_TETRA_USW);
+  PUSH (4, LAMENT_TETRA_DWN);
+  PUSH (4, LAMENT_TETRA_DSE);
+
+  PUSH (8, LAMENT_STAR_OUT);           /* pretty common */
+  PUSH (8, LAMENT_TASER_OUT);
+  PUSH (8, LAMENT_PILLAR_OUT);
+
+  PUSH (4, LAMENT_LID_OPEN);           /* rare */
+  PUSH (2, LAMENT_SPHERE_OUT);         /* rare */
+  PUSH (1, LAMENT_LEVIATHAN_SPIN);     /* very rare */
+
+  PUSH (35, LAMENT_BOX);               /* rest state */
+# undef PUSH
+
+  shuffle_states (lc);
+
+# ifdef DEBUG_MODE
+  lc->type = DEBUG_MODE;
+  lc->anim_pause = 0;
+# endif
+
 }
 
 
-void
-draw_lament(ModeInfo *mi)
+ENTRYPOINT void
+draw_lament (ModeInfo *mi)
 {
-  static int tick = 0;
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
   Display *dpy = MI_DISPLAY(mi);
   Window window = MI_WINDOW(mi);
@@ -2087,26 +1770,17 @@ draw_lament(ModeInfo *mi)
 
   glXMakeCurrent(dpy, window, *(lc->glx_context));
   draw(mi);
+  if (mi->fps_p) do_fps (mi);
+
   glFinish();
   glXSwapBuffers(dpy, window);
 
-  if (lc->type != LAMENT_LID_ZOOM)
-    {
-      rotate(&lc->rotx, &lc->dx, &lc->ddx, lc->d_max);
-      rotate(&lc->roty, &lc->dy, &lc->ddy, lc->d_max);
-      rotate(&lc->rotz, &lc->dz, &lc->ddz, lc->d_max);
-    }
-
-  if (lc->anim_pause)
+  if (!lc->ffwdp && lc->anim_pause)
     lc->anim_pause--;
   else
     animate(mi);
-
-  if (++tick > 500)
-    {
-      tick = 0;
-      reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
-    }
 }
 
+XSCREENSAVER_MODULE ("Lament", lament)
+
 #endif /* USE_GL */