From http://www.jwz.org/xscreensaver/xscreensaver-5.15.tar.gz
[xscreensaver] / hacks / glx / tronbit.c
diff --git a/hacks/glx/tronbit.c b/hacks/glx/tronbit.c
new file mode 100644 (file)
index 0000000..98cbf28
--- /dev/null
@@ -0,0 +1,553 @@
+/* tronbit, Copyright (c) 2011 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
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ */
+
+#define DEFAULTS       "*delay:        30000       \n" \
+                       "*count:        30          \n" \
+                       "*showFPS:      False       \n" \
+                       "*wireframe:    False       \n"
+
+# define refresh_bit 0
+# define release_bit 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "colors.h"
+#include "sphere.h"
+#include "rotator.h"
+#include "gltrackball.h"
+#include <ctype.h>
+
+#ifdef USE_GL /* whole file */
+
+#include "gllist.h"
+
+extern const struct gllist *tronbit_idle1, *tronbit_idle2,
+  *tronbit_no, *tronbit_yes;
+static const struct gllist * const *all_objs[] = {
+  &tronbit_idle1, &tronbit_idle2, &tronbit_no, &tronbit_yes };
+
+
+#define DEF_SPIN        "True"
+#define DEF_WANDER      "True"
+#define DEF_SPEED       "1.0"
+
+#define HISTORY_LENGTH 512
+typedef enum { BIT_IDLE1, BIT_IDLE2, BIT_NO, BIT_YES } bit_state;
+#define MODELS 4
+
+
+typedef struct {
+  GLXContext *glx_context;
+  rotator *rot;
+  trackball_state *trackball;
+  Bool button_down_p;
+
+  double frequency;
+  double confidence;
+
+  double last_time;
+  unsigned char history   [HISTORY_LENGTH];
+  unsigned char histogram [HISTORY_LENGTH];
+  int history_fp, histogram_fp;
+
+  GLuint dlists[MODELS], polys[MODELS];
+  char kbd;
+
+} bit_configuration;
+
+static bit_configuration *bps = NULL;
+
+static const GLfloat colors[][4] = {
+  { 0.66, 0.85, 1.00, 1.00 },
+  { 0.66, 0.85, 1.00, 1.00 },
+  { 1.00, 0.12, 0.12, 1.00 },
+  { 0.98, 0.85, 0.30, 1.00 }
+};
+
+
+static Bool do_spin;
+static GLfloat speed;
+static Bool do_wander;
+
+static XrmOptionDescRec opts[] = {
+  { "-spin",   ".spin",   XrmoptionNoArg, "True" },
+  { "+spin",   ".spin",   XrmoptionNoArg, "False" },
+  { "-speed",  ".speed",  XrmoptionSepArg, 0 },
+  { "-wander", ".wander", XrmoptionNoArg, "True" },
+  { "+wander", ".wander", XrmoptionNoArg, "False" }
+};
+
+static argtype vars[] = {
+  {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
+  {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
+  {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
+};
+
+ENTRYPOINT ModeSpecOpt bit_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+
+/* Returns the current time in seconds as a double.
+ */
+static double
+double_time (void)
+{
+  struct timeval now;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+  struct timezone tzp;
+  gettimeofday(&now, &tzp);
+# else
+  gettimeofday(&now);
+# endif
+
+  return (now.tv_sec + ((double) now.tv_usec * 0.000001));
+}
+
+
+static int
+make_bit (ModeInfo *mi, bit_state which)
+{
+  static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
+  static const GLfloat shiny   = 128.0;
+  const GLfloat *color = colors[which];
+  int wire = MI_IS_WIREFRAME(mi);
+  int polys = 0;
+  GLfloat s;
+  const struct gllist *gll;
+
+  glMaterialfv (GL_FRONT, GL_SPECULAR,            spec);
+  glMateriali  (GL_FRONT, GL_SHININESS,           shiny);
+  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
+  glColor4f (color[0], color[1], color[2], color[3]);
+
+  glPushMatrix();
+  switch (which)
+    {
+    case BIT_IDLE1:
+      glRotatef (-44, 0, 1, 0);   /* line up the models with each other */
+      glRotatef (-11, 1, 0, 0);
+      glRotatef (  8, 0, 0, 1);
+      s = 1.0;
+      break;
+    case BIT_IDLE2:
+      glRotatef ( 16.0, 0, 0, 1);
+      glRotatef (-28.0, 1, 0, 0);
+      s = 1.0;
+      break;
+    case BIT_NO:
+      glRotatef ( 16.0, 0, 0, 1);
+      glRotatef (-28.0, 1, 0, 0);
+      s = 1.6;
+      break;
+    case BIT_YES:
+      glRotatef (-44.0, 0, 1, 0);
+      glRotatef (-32.0, 1, 0, 0);
+      s = 1.53;
+      break;
+    default:
+      abort();
+      break;
+    }
+  glScalef (s, s, s);
+  gll = *all_objs[which];
+  renderList (gll, wire);
+  polys += gll->points / 3;
+  glPopMatrix();
+
+  return polys;
+}
+
+
+static void
+tick_bit (ModeInfo *mi, double now)
+{
+  bit_configuration *bp = &bps[MI_SCREEN(mi)];
+  double freq = bp->frequency;
+  int n = bp->history[bp->history_fp];
+  int histogram_speed = 3 * speed;
+  int i;
+
+  if (histogram_speed < 1) histogram_speed = 1;
+
+  if (n == BIT_YES || n == BIT_NO)
+    freq *= 2;
+
+  if (bp->button_down_p) return;
+
+  for (i = 0; i < histogram_speed; i++)
+    {
+      int nn = (n == BIT_YES ? 240 : n == BIT_NO  ? 17 : 128);
+      int on = bp->histogram[(bp->histogram_fp-1) % countof(bp->histogram)];
+
+      /* smooth out the square wave a little bit */
+
+      if (!(nn > 100 && nn < 200) !=
+          !(on > 100 && on < 200))
+        nn += (((random() % 48) - 32) *
+               ((on > 100 && on < 200) ? 1 : -1));
+
+      nn += (random() % 16) - 8;
+
+      bp->histogram_fp++;
+      if (bp->histogram_fp >= countof(bp->history))
+        bp->histogram_fp = 0;
+      bp->histogram [bp->histogram_fp] = nn;
+    }
+
+
+  if (bp->last_time + freq > now && !bp->kbd) return;
+
+  bp->last_time = now;
+
+  bp->history_fp++;
+  if (bp->history_fp >= countof(bp->history))
+    bp->history_fp = 0;
+
+  if (bp->kbd)
+    {
+      n = (bp->kbd == '1' ? BIT_YES :
+           bp->kbd == '0' ? BIT_NO :
+           (random() & 1) ? BIT_YES : BIT_NO);
+      bp->kbd = 0;
+    }
+  else if (n == BIT_YES || 
+           n == BIT_NO ||
+           frand(1.0) >= bp->confidence)
+    n = (n == BIT_IDLE1 ? BIT_IDLE2 : BIT_IDLE1);
+  else
+    n = (random() & 1) ? BIT_YES : BIT_NO;
+
+  bp->history [bp->history_fp] = n;
+}
+
+
+static int
+animate_bits (ModeInfo *mi, bit_state omodel, bit_state nmodel, GLfloat ratio)
+{
+  bit_configuration *bp = &bps[MI_SCREEN(mi)];
+  int polys = 0;
+  GLfloat scale = sin (ratio * M_PI / 2);
+  GLfloat osize, nsize, small;
+
+ if ((omodel == BIT_IDLE1 || omodel == BIT_IDLE2) &&
+     (nmodel == BIT_IDLE1 || nmodel == BIT_IDLE2))
+   small = 0.9;
+ else
+   small = 0.5;
+
+  nsize = small + (1 - small) * scale;
+  osize = small + (1 - small) * (1 - scale);
+
+  glPushMatrix();
+  glScalef (osize, osize, osize);
+  glCallList (bp->dlists [omodel]);
+  polys += bp->polys [omodel];
+  glPopMatrix();
+
+  glPushMatrix();
+  glScalef (nsize, nsize, nsize);
+  glCallList (bp->dlists [nmodel]);
+  polys += bp->polys [nmodel];
+  glPopMatrix();
+
+  return polys;
+}
+
+
+static int
+draw_histogram (ModeInfo *mi, GLfloat ratio)
+{
+  bit_configuration *bp = &bps[MI_SCREEN(mi)];
+  int samples = countof (bp->histogram);
+  GLfloat scalex = (GLfloat) mi->xgwa.width / samples;
+  GLfloat scaley = mi->xgwa.height / 255.0 / 4;  /* about 1/4th of screen */
+  int polys = 0;
+  int overlays = 5;
+  int k;
+  
+  glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
+                GL_ENABLE_BIT |     /* for various glDisable calls */
+                GL_CURRENT_BIT |    /* for glColor3f() */
+                GL_LIST_BIT);       /* for glListBase() */
+
+  glDisable (GL_TEXTURE_2D);
+  glDisable (GL_LIGHTING);
+  glDisable (GL_BLEND);
+  glDisable (GL_DEPTH_TEST);
+  glDisable (GL_CULL_FACE);
+
+  glMatrixMode(GL_PROJECTION);
+  glPushMatrix();
+  {
+    glLoadIdentity();
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+
+    glLoadIdentity();
+    gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
+
+    for (k = 0; k < overlays; k++)
+      {
+        int i, j;
+        GLfloat a = (GLfloat) k / overlays;
+
+        glColor3f (0.3 * a, 0.7 * a, 1.0 * a);
+
+        glBegin (GL_LINE_STRIP);
+
+        j = bp->histogram_fp + 1;
+        for (i = 0; i < samples; i++)
+          {
+            GLfloat x = i;
+            GLfloat y = bp->histogram[j];
+            GLfloat z = 0;
+
+            y += (int) ((random() % 16) - 8);
+            y += 16;  /* margin at bottom of screen */
+
+            x *= scalex;
+            y *= scaley;
+
+            glVertex3f (x, y, z);
+            if (++j >= samples) j = 0;
+            polys++;
+          }
+        glEnd();
+      }
+
+    glPopMatrix();
+  }
+  glMatrixMode(GL_PROJECTION);
+  glPopMatrix();
+
+  glPopAttrib();
+
+  glMatrixMode(GL_MODELVIEW);
+
+  return polys;
+}
+
+
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_bit (ModeInfo *mi, int width, int height)
+{
+  GLfloat h = (GLfloat) height / (GLfloat) width;
+
+  glViewport (0, 0, (GLint) width, (GLint) height);
+
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  gluPerspective (30.0, 1/h, 1.0, 100.0);
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  gluLookAt( 0.0, 0.0, 30.0,
+             0.0, 0.0, 0.0,
+             0.0, 1.0, 0.0);
+
+  glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+
+ENTRYPOINT Bool
+bit_handle_event (ModeInfo *mi, XEvent *event)
+{
+  bit_configuration *bp = &bps[MI_SCREEN(mi)];
+
+  if (event->xany.type == ButtonPress &&
+      event->xbutton.button == Button1)
+    {
+      bp->button_down_p = True;
+      gltrackball_start (bp->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)
+    {
+      bp->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 (bp->trackball, event->xbutton.button, 3,
+                              !!event->xbutton.state);
+      return True;
+    }
+  else if (event->xany.type == MotionNotify &&
+           bp->button_down_p)
+    {
+      gltrackball_track (bp->trackball,
+                         event->xmotion.x, event->xmotion.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+  else if (event->xany.type == KeyPress)
+    {
+      KeySym keysym;
+      char c = 0;
+      XLookupString (&event->xkey, &c, 1, &keysym, 0);
+      if (c == ' ' || c == '1' || c == '0')
+        {
+          bp->kbd = c;
+          return True;
+        }
+    }
+
+  return False;
+}
+
+
+ENTRYPOINT void 
+init_bit (ModeInfo *mi)
+{
+  bit_configuration *bp;
+  int wire = MI_IS_WIREFRAME(mi);
+  int i;
+
+  if (!bps) {
+    bps = (bit_configuration *)
+      calloc (MI_NUM_SCREENS(mi), sizeof (bit_configuration));
+    if (!bps) {
+      fprintf(stderr, "%s: out of memory\n", progname);
+      exit(1);
+    }
+  }
+
+  bp = &bps[MI_SCREEN(mi)];
+
+  bp->glx_context = init_GL(mi);
+
+  reshape_bit (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+  if (!wire)
+    {
+      GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
+      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
+      GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
+      GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
+
+      glEnable(GL_LIGHTING);
+      glEnable(GL_LIGHT0);
+      glEnable(GL_DEPTH_TEST);
+      glEnable(GL_CULL_FACE);
+
+      glLightfv(GL_LIGHT0, GL_POSITION, pos);
+      glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
+      glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
+      glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
+    }
+
+  {
+    double spin_speed   = 3.0;
+    double wander_speed = 0.03 * speed;
+    double spin_accel   = 4.0;
+
+    bp->rot = make_rotator (do_spin ? spin_speed : 0,
+                            do_spin ? spin_speed : 0,
+                            do_spin ? spin_speed : 0,
+                            spin_accel,
+                            do_wander ? wander_speed : 0,
+                            False);
+    bp->trackball = gltrackball_init ();
+  }
+
+  for (i = 0; i < countof(bp->dlists); i++)
+    {
+      bp->dlists[i] = glGenLists (1);
+      glNewList (bp->dlists[i], GL_COMPILE);
+      bp->polys [i] = make_bit (mi, i);
+      glEndList ();
+    }
+
+  bp->frequency  = 0.30 / speed;       /* parity around 3x/second */
+  bp->confidence = 0.06;               /* provide answer 1/15 or so */
+
+  for (i = 0; i < countof(bp->histogram); i++)
+    bp->histogram[i] = 128 + (random() % 16) - 8;
+}
+
+
+ENTRYPOINT void
+draw_bit (ModeInfo *mi)
+{
+  bit_configuration *bp = &bps[MI_SCREEN(mi)];
+  Display *dpy = MI_DISPLAY(mi);
+  Window window = MI_WINDOW(mi);
+
+  if (!bp->glx_context)
+    return;
+
+  glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
+
+  glShadeModel(GL_SMOOTH);
+
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_NORMALIZE);
+  glEnable(GL_CULL_FACE);
+
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glPushMatrix ();
+
+  glScalef(1.1, 1.1, 1.1);
+
+  {
+    double x, y, z;
+    get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
+    glTranslatef((x - 0.5) * 11,
+                 (y - 0.5) * 5,
+                 (z - 0.5) * 3);
+
+    gltrackball_rotate (bp->trackball);
+
+    get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
+    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);
+  }
+
+  mi->polygon_count = 0;
+
+  glScalef (6, 6, 6);
+
+
+  {
+    int nmodel = bp->history [bp->history_fp];
+    int omodel = bp->history [bp->history_fp > 0
+                              ? bp->history_fp-1
+                              : countof(bp->history)-1];
+    double now = double_time();
+    double ratio = 1 - ((bp->last_time + bp->frequency) - now) / bp->frequency;
+    if (ratio > 1) ratio = 1;
+    mi->polygon_count += draw_histogram (mi, ratio);
+    mi->polygon_count += animate_bits (mi, omodel, nmodel, ratio);
+    tick_bit (mi, now);
+  }
+  glPopMatrix ();
+
+  if (mi->fps_p) do_fps (mi);
+  glFinish();
+
+  glXSwapBuffers(dpy, window);
+}
+
+XSCREENSAVER_MODULE_2 ("TronBit", tronbit, bit)
+
+#endif /* USE_GL */