From http://www.jwz.org/xscreensaver/xscreensaver-5.18.tar.gz
[xscreensaver] / hacks / glx / lament.c
index 3f5080c04d0b0b0650b988c7df94d29c732c7850..ab4bf83e1109ca7d472c34538b13459a8202f401 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1998 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1998-2012 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
         "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.
+        surfaces.  Not sure why.
 
-     *  Perhaps use a slightly-bumpy or oily texture for the interior surfaces?
+     *  Should use a dark wood-grain 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.
+     *  Building a face out of multiple adjacent triangles was a terrible
+        idea and leads to visible seams.  Should re-do the face generation
+        to make all of them out of a single triangle strip instead.
+
+     *  The coordinates are slightly off from the image.  lament512.gif is the
+        "right" one, and "lament512b.gif" is the image corrected to line up
+        with what the code is actually doing.
+
+     *  The "star" slices really don't line up well.
 
      *  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 
         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.
-
      *  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.
         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)?
-
      *  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 HACK_RESHAPE   reshape_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"
+                       "*wireframe:    False   \n"
+# define refresh_lament 0
+# define release_lament 0
 #include "xlockmore.h"
 
 #ifdef USE_GL /* whole file */
 #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 "normals.h"
 #include "xpm-ximage.h"
-#include "../images/lament.xpm"
+#include "rotator.h"
+#include "gltrackball.h"
+#if 0
+# include "../images/lament128.xpm"
+#else
+# include "../images/lament512.xpm"
+#endif
 
 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
 #define RANDSIGN() ((random() & 1) ? 1 : -1)
@@ -140,20 +125,26 @@ typedef enum {
 
 } lament_type;
 
-static 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 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 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  */
+ };
 
 
 typedef struct {
   GLXContext *glx_context;
+  rotator *rot;
+  double rotx, roty, rotz;
+  trackball_state *trackball;
+  Bool button_down_p;
 
   GLuint box;                     /* display list IDs */
   GLuint star1, star2;
@@ -161,16 +152,16 @@ typedef struct {
   GLuint lid_0, lid_1, lid_2, lid_3, lid_4;
   GLuint taser_base, taser_lifter, taser_slider;
 
-  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 */
   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;
 
@@ -193,99 +184,12 @@ parse_image_data(ModeInfo *mi)
                               lament_faces);
 }
 
-\f
-/* Computing normal vectors (thanks to Nat Friedman <ndf@mit.edu>)
- */
-
-typedef struct vector {
-  GLfloat x, y, z;
-} vector;
-
-typedef struct plane {
-  vector p1, p2, p3;
-} plane;
-
-static void
-vector_set(vector *v, GLfloat x, GLfloat y, GLfloat z)
-{
-  v->x = x;
-  v->y = y;
-  v->z = z;
-}
-
-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);
-}
-
-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;
-}
-
-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);
-}
-
-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 */
-}
-
-
 \f
 /* Shorthand utilities for making faces, with proper normals.
  */
 
 static void
-set_colors (GLfloat *color)
+set_colors (const GLfloat *color)
 {
   glMaterialfv(GL_FRONT, GL_AMBIENT, color+0);
   glMaterialfv(GL_FRONT, GL_DIFFUSE, color+4);
@@ -294,7 +198,7 @@ set_colors (GLfloat *color)
 }
 
 static void
-face3(GLint texture, GLfloat *color, Bool wire,
+face3(GLint texture, const 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)
@@ -313,7 +217,7 @@ face3(GLint texture, GLfloat *color, Bool wire,
 }
 
 static void
-face4(GLint texture, GLfloat *color, Bool wire,
+face4(GLint texture, const 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,
@@ -333,7 +237,7 @@ face4(GLint texture, GLfloat *color, Bool wire,
 }
 
 static void
-face5(GLint texture, GLfloat *color, Bool wire,
+face5(GLint texture, const 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,
@@ -420,8 +324,8 @@ star(ModeInfo *mi, Bool top, Bool wire)
   int i;
 
   int points[][2] = {
-    {  77,  74 }, {  60,  98 }, {   0,  71 }, {   0,   0 },    /* L1 */
-    {  60,  98 }, {  55, 127 }, {   0, 127 }, {   0,  71 },    /* L2 */
+    {  77,  74 }, {  60,  99 }, {   0,  74 }, {   0,   0 },    /* L1 */
+    {  60,  99 }, {  55, 127 }, {   0, 127 }, {   0,  74 },    /* 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 */
@@ -1403,6 +1307,108 @@ taser(ModeInfo *mi, Bool wire)
 /* Rendering and animating object models
  */
 
+ENTRYPOINT Bool
+lament_handle_event (ModeInfo *mi, XEvent *event)
+{
+  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+
+  if (event->xany.type == ButtonPress &&
+      event->xbutton.button == Button1)
+    {
+      lc->button_down_p = True;
+      gltrackball_start (lc->trackball,
+                         event->xbutton.x, event->xbutton.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+  else if (event->xany.type == ButtonRelease &&
+           event->xbutton.button == Button1)
+    {
+      lc->button_down_p = False;
+      return True;
+    }
+  else if (event->xany.type == ButtonPress &&
+           (event->xbutton.button == Button4 ||
+            event->xbutton.button == Button5 ||
+            event->xbutton.button == Button6 ||
+            event->xbutton.button == Button7))
+    {
+      gltrackball_mousewheel (lc->trackball, event->xbutton.button, 5,
+                              !!event->xbutton.state);
+      return True;
+    }
+  else if (event->xany.type == MotionNotify &&
+           lc->button_down_p)
+    {
+      gltrackball_track (lc->trackball,
+                         event->xmotion.x, event->xmotion.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+
+  return False;
+}
+
+
+static void
+check_facing(ModeInfo *mi)
+{
+  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+
+  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 (-5, 0, 0, m, p, v, &x, &y, &z);
+  x = (x / MI_WIDTH(mi))  - 0.5;
+  y = (y / MI_HEIGHT(mi)) - 0.5;
+  lc->facing_p = (z < 0.9 &&
+                  x > -0.02 && x < 0.02 &&
+                  y > -0.02 && y < 0.02);
+}
+
+
+
+static void
+scale_for_window(ModeInfo *mi)
+{
+  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+  int target_size = lc->texture->width * 1.4;
+  int win_size = (MI_WIDTH(mi) > MI_HEIGHT(mi) ? MI_HEIGHT(mi) : MI_WIDTH(mi));
+
+  /* This scale makes the box take up most of the window */
+  glScalef(8, 8, 8);
+
+  /* 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 pixelated.
+   */
+
+  {
+    int max = 340;               /* Let's not go larger than life-sized. */
+    if (target_size > max)
+      target_size = max;
+  }
+
+  if (win_size > 640 &&
+      win_size > target_size * 1.5)
+    {
+      GLfloat ratio = ((GLfloat) target_size / (GLfloat) win_size);
+      ratio *= 2.0;
+      glScalef(ratio, ratio, ratio);
+    }
+}
+
+
 static void
 draw(ModeInfo *mi)
 {
@@ -1415,26 +1421,18 @@ draw(ModeInfo *mi)
     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
+  /* Do it twice because we don't track the device's orientation. */
+  glRotatef( current_device_rotation(), 0, 0, 1);
+  gltrackball_rotate (lc->trackball);
+  glRotatef(-current_device_rotation(), 0, 0, 1);
 
-    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);
 
-    /* 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);
 
-    /* Scale it up. */
-    glScalef(4.0, 4.0, 4.0);
-
-#ifdef DEBUG
+#if 0
     glPushMatrix();
     {
       /* Shift to the upper left, and draw the vanilla box. */
@@ -1452,15 +1450,20 @@ draw(ModeInfo *mi)
 
     /* Shift to the lower right, and draw the animated object. */
     glTranslatef(0.6, 0.0, -0.6);
-#endif /* DEBUG */
+#endif /* 0 */
 
 
     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);
+      if (lc->type != LAMENT_LID_ZOOM)
+        get_rotation (lc->rot, &lc->rotx, &lc->roty, &lc->rotz,
+                      !lc->button_down_p);
+      glRotatef (lc->rotx * 360, 1.0, 0.0, 0.0);
+      glRotatef (lc->roty * 360, 0.0, 1.0, 0.0);
+      glRotatef (lc->rotz * 360, 0.0, 0.0, 1.0);
+
+      check_facing(mi);
 
       switch (lc->type)
        {
@@ -1570,11 +1573,29 @@ draw(ModeInfo *mi)
     }
     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.)
+ */
+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)
 {
@@ -1587,38 +1608,13 @@ animate(ModeInfo *mi)
     {
     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;
@@ -1724,28 +1720,11 @@ animate(ModeInfo *mi)
 
       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->type = (lc->facing_p ? LAMENT_LID_ZOOM : LAMENT_LID_CLOSE);
+        }
       break;
 
     case LAMENT_LID_CLOSE:
@@ -1764,9 +1743,6 @@ animate(ModeInfo *mi)
        {
          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;
@@ -1820,116 +1796,22 @@ animate(ModeInfo *mi)
 }
 
 
-static void
-rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
-{
-  double ppos = *pos;
-
-  /* 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;
-
-  if (ppos < 0) abort();
-  if (ppos > 1.0) abort();
-  *pos = (*pos > 0 ? ppos : -ppos);
-
-  /* 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)
-       {
-         *v = 0;
-
-         /* keep going in the same direction */
-         if (random() % 2)
-           *dv = 0;
-         else if (*dv < 0)
-           *dv = -*dv;
-       }
-      else
-       {
-         /* reverse gears */
-         *v = -*v;
-         *dv = -*dv;
-         *pos = -*pos;
-       }
-    }
-
-  /* Alter direction of rotational acceleration randomly. */
-  if (! (random() % 120))
-    *dv = -*dv;
-
-  /* Change acceleration very occasionally. */
-  if (! (random() % 200))
-    {
-      if (*dv == 0)
-       *dv = 0.00001;
-      else if (random() & 1)
-       *dv *= 1.2;
-      else
-       *dv *= 0.8;
-    }
-}
-
-
 \f
 /* Window management, etc
  */
 
-void
+ENTRYPOINT void
 reshape_lament(ModeInfo *mi, int width, int height)
 {
-  int target_size = 180;
-  int win_size = (width > height ? height : width);
   GLfloat h = (GLfloat) height / (GLfloat) width;
-
   glViewport(0, 0, (GLint) width, (GLint) height);
 
-/*  glViewport(-600, -600, 1800, 1800); */
-
   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);
-
-  /* This scale makes the box take up most of the window */
-  glScalef(2.0, 2.0, 2.0);
-
-  /* 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)
-    {
-      GLfloat ratio = ((GLfloat) target_size / (GLfloat) win_size);
-      ratio *= 2.0;
-      glScalef(ratio, ratio, ratio);
-    }
-
-  /* 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);
 }
 
@@ -1945,13 +1827,13 @@ gl_init(ModeInfo *mi)
 
   if (!wire)
     {
-      static GLfloat pos0[]  = { -4.0,  2.0, 5.0, 1.0 };
-      static GLfloat pos1[]  = {  6.0, -1.0, 3.0, 1.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 GLfloat amb0[]  = { 0.7, 0.7, 0.7, 1.0 };
-/*    static GLfloat amb1[]  = { 0.7, 0.0, 0.0, 1.0 }; */
-      static GLfloat dif0[]  = { 1.0, 1.0, 1.0, 1.0 };
-      static GLfloat dif1[]  = { 0.3, 0.1, 0.1, 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);
@@ -1982,7 +1864,8 @@ gl_init(ModeInfo *mi)
       parse_image_data(mi);
 
       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++)
        {
@@ -1993,7 +1876,9 @@ gl_init(ModeInfo *mi)
           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)));
           check_gl_error("texture");
@@ -2002,8 +1887,6 @@ gl_init(ModeInfo *mi)
          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);
        }
 
 #else  /* !HAVE_GLBINDTEXTURE */
@@ -2082,7 +1965,7 @@ handle_signals (void)
 # endif /* HAVE_MESA_GL */
 
 
-void
+ENTRYPOINT void
 init_lament(ModeInfo *mi)
 {
   lament_configuration *lc;
@@ -2099,20 +1982,11 @@ init_lament(ModeInfo *mi)
 
   lc = &lcs[MI_SCREEN(mi)];
 
-  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);
-
-  lc->d_max = lc->dx * 2;
-
-  lc->ddx = 0.00006 + frand(0.00003);
-  lc->ddy = 0.00006 + frand(0.00003);
-  lc->ddz = 0.00006 + frand(0.00003);
+  {
+    double rot_speed = 0.5;
+    lc->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
+    lc->trackball = gltrackball_init ();
+  }
 
   lc->type = LAMENT_BOX;
   lc->anim_pause = 300 + (random() % 100);
@@ -2123,13 +1997,38 @@ init_lament(ModeInfo *mi)
       gl_init(mi);
     }
 
+  lc->states = (lament_type *) calloc (50, sizeof (*lc->states));
+  lc->nstates = 0;
+  lc->states[lc->nstates++] = LAMENT_STAR_OUT;
+  lc->states[lc->nstates++] = LAMENT_STAR_OUT;
+  lc->states[lc->nstates++] = LAMENT_TETRA_UNE;
+  lc->states[lc->nstates++] = LAMENT_TETRA_USW;
+  lc->states[lc->nstates++] = LAMENT_TETRA_DWN;
+  lc->states[lc->nstates++] = LAMENT_TETRA_DSE;
+  lc->states[lc->nstates++] = LAMENT_LID_OPEN;
+  lc->states[lc->nstates++] = LAMENT_LID_OPEN;
+  lc->states[lc->nstates++] = LAMENT_LID_OPEN;
+  lc->states[lc->nstates++] = LAMENT_TASER_OUT;
+  lc->states[lc->nstates++] = LAMENT_TASER_OUT;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  shuffle_states (lc);
+
 # ifdef HAVE_MESA_GL
   handle_signals ();
 # endif /* HAVE_MESA_GL */
 }
 
 
-void
+ENTRYPOINT void
 draw_lament(ModeInfo *mi)
 {
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
@@ -2148,17 +2047,12 @@ draw_lament(ModeInfo *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)
     lc->anim_pause--;
   else
     animate(mi);
 }
 
+XSCREENSAVER_MODULE ("Lament", lament)
+
 #endif /* USE_GL */