From http://www.jwz.org/xscreensaver/xscreensaver-5.36.tar.gz
[xscreensaver] / hacks / glx / hexstrut.c
diff --git a/hacks/glx/hexstrut.c b/hacks/glx/hexstrut.c
new file mode 100644 (file)
index 0000000..f264609
--- /dev/null
@@ -0,0 +1,512 @@
+/* hexstrut, Copyright (c) 2016 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" \
+                       "*showFPS:      False       \n" \
+                       "*wireframe:    False       \n" \
+                       "*count:        20          \n" \
+                       "*suppressRotationAnimation: True\n" \
+
+# define refresh_hexstrut 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "colors.h"
+#include "normals.h"
+#include "rotator.h"
+#include "gltrackball.h"
+#include <ctype.h>
+
+#ifdef USE_GL /* whole file */
+
+
+#define DEF_SPIN        "True"
+#define DEF_WANDER      "True"
+#define DEF_SPEED       "1.0"
+#define DEF_THICKNESS   "0.2"
+
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
+
+typedef struct { double a, o; } LL;    /* latitude + longitude */
+
+typedef struct triangle triangle;
+struct triangle {
+  XYZ p[3];
+  triangle *next;
+  triangle *neighbors[6];
+  GLfloat orot, rot;
+  int delay, odelay;
+  int ccolor;
+};
+
+typedef struct {
+  GLXContext *glx_context;
+  rotator *rot;
+  trackball_state *trackball;
+  Bool button_down_p;
+
+  int count;
+  triangle *triangles;
+
+  int ncolors;
+  XColor *colors;
+
+} hexstrut_configuration;
+
+static hexstrut_configuration *bps = NULL;
+
+static Bool do_spin;
+static GLfloat speed;
+static Bool do_wander;
+static GLfloat thickness;
+
+static XrmOptionDescRec opts[] = {
+  { "-spin",   ".spin",   XrmoptionNoArg, "True" },
+  { "+spin",   ".spin",   XrmoptionNoArg, "False" },
+  { "-speed",  ".speed",  XrmoptionSepArg, 0 },
+  { "-wander", ".wander", XrmoptionNoArg, "True" },
+  { "+wander", ".wander", XrmoptionNoArg, "False" },
+  { "-thickness", ".thickness", XrmoptionSepArg, 0 },
+};
+
+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},
+  {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
+};
+
+ENTRYPOINT ModeSpecOpt hexstrut_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+
+
+/* Add t1 to the neighbor list of t0. */
+static void
+link_neighbor (triangle *t0, triangle *t1)
+{
+  int k;
+  if (t0 == t1)
+    return;
+  for (k = 0; k < countof(t0->neighbors); k++)
+    {
+      if (t0->neighbors[k] == t1 ||
+          t0->neighbors[k] == 0)
+        {
+          t0->neighbors[k] = t1;
+          return;
+        }
+    }
+  fprintf (stderr, "too many neighbors\n");
+  abort();
+}
+
+
+static void
+make_plane (ModeInfo *mi)
+{
+  hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
+  int n = MI_COUNT(mi) * 2;
+  GLfloat size = 2.0 / n;
+  int x, y;
+  GLfloat w = size;
+  GLfloat h = size * sqrt(3) / 2;
+  triangle **grid = (triangle **) calloc (n * n, sizeof(*grid));
+
+  for (y = 0; y < n; y++)
+    for (x = 0; x < n; x++)
+      {
+        triangle *t;
+
+        t = (triangle *) calloc (1, sizeof(*t));
+        t->p[0].x = (x - n/2) * w;
+        t->p[0].y = (y - n/2) * h;
+
+        if (y & 1)
+          t->p[0].x += w / 2;
+
+        t->p[1].x = t->p[0].x - w/2;
+        t->p[2].x = t->p[0].x + w/2;
+        t->p[1].y = t->p[0].y + h;
+        t->p[2].y = t->p[0].y + h;
+
+        if (x > 0)
+          {
+            triangle *t2 = grid[y * n + (x-1)];
+            link_neighbor (t, t2);
+            link_neighbor (t2, t);
+          }
+        if (y > 0)
+          {
+            triangle *t2 = grid[(y-1) * n + x];
+            link_neighbor (t, t2);
+            link_neighbor (t2, t);
+
+            if (x < n-1)
+              {
+                t2 = grid[(y-1) * n + (x+1)];
+                link_neighbor (t, t2);
+                link_neighbor (t2, t);
+              }
+          }
+
+        t->next = bp->triangles;
+        bp->triangles = t;
+        grid[y * n + x] = t;
+        bp->count++;
+      }
+
+  free (grid);
+}
+
+
+static void
+tick_triangles (ModeInfo *mi)
+{
+  hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
+  triangle *t;
+  GLfloat step = 0.01 + (0.04 * speed);
+
+  if (! (random() % 80))
+    {
+      int n = random() % bp->count;
+      int i;
+      for (i = 0, t = bp->triangles; t && i < n; t = t->next, i++)
+        ;
+      if (! t->rot)
+        {
+          t->rot += step * ((random() & 1) ? 1 : -1);
+          t->odelay = t->delay = 4;
+        }
+    }
+
+  for (t = bp->triangles; t; t = t->next)
+    {
+      /* If this triangle is rotating, continue until done. */
+      if (t->rot)
+        {
+          t->rot += step * (t->rot > 0 ? 1 : -1);
+
+          t->ccolor++;
+          if (t->ccolor > bp->ncolors)
+            t->ccolor = 0;
+
+          if (t->rot > 1 || t->rot < -1)
+            {
+              t->orot += (t->rot > 1 ? 1 : -1);
+              t->rot = 0;
+            }
+        }
+
+      /* If this triangle's propagation delay hasn't hit zero, decrement it.
+         When it does, start its neighbors rotating.
+       */
+      if (t->delay)
+        {
+          int i;
+          t->delay--;
+          if (t->delay == 0)
+            for (i = 0; i < countof(t->neighbors); i++)
+              {
+                if (t->neighbors[i] &&
+                    t->neighbors[i]->rot == 0)
+                  {
+                    t->neighbors[i]->rot += step * (t->rot > 0 ? 1 : -1);
+                    t->neighbors[i]->delay = 
+                      t->neighbors[i]->odelay = t->odelay;
+                  }
+              }
+        }
+    }
+}
+
+
+static void
+draw_triangles (ModeInfo *mi)
+{
+  hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
+  int wire = MI_IS_WIREFRAME(mi);
+  triangle *t;
+  GLfloat length = sqrt(3) / 3;
+  GLfloat t2 = length * thickness / 2;
+  GLfloat scale;
+
+  {
+    triangle *t = bp->triangles;
+    GLfloat X = t->p[0].x - t->p[1].x;
+    GLfloat Y = t->p[0].y - t->p[1].y;
+    GLfloat Z = t->p[0].z - t->p[1].z;
+    scale = sqrt(X*X + Y*Y + Z*Z);
+  }
+
+  glFrontFace (GL_CCW);
+
+  glBegin (wire ? GL_LINES : GL_QUADS);
+
+  glNormal3f (0, 0, 1);
+  for (t = bp->triangles; t; t = t->next)
+    {
+      int i;
+      XYZ c;
+      GLfloat color[4];
+
+      GLfloat angle = (M_PI * 2 / 3) * t->rot;
+      GLfloat cr = cos(angle), sr = sin(angle);
+
+      c.x = (t->p[0].x + t->p[1].x + t->p[2].x) / 3;
+      c.y = (t->p[0].y + t->p[1].y + t->p[2].y) / 3;
+      c.z = (t->p[0].z + t->p[1].z + t->p[2].z) / 3;
+
+      /* Actually we don't need normals at all, since no lighting.
+      do_normal (t->p[0].x, t->p[0].y, t->p[0].z,
+                 t->p[1].x, t->p[1].y, t->p[1].z,
+                 t->p[2].x, t->p[2].y, t->p[2].z);
+      */
+
+      color[0] = bp->colors[t->ccolor].red   / 65535.0;
+      color[1] = bp->colors[t->ccolor].green / 65535.0;
+      color[2] = bp->colors[t->ccolor].blue  / 65535.0;
+      color[3] = 1;
+
+      /* Brighter */
+      color[0] = color[0] * 0.75 + 0.25;
+      color[1] = color[1] * 0.75 + 0.25;
+      color[2] = color[2] * 0.75 + 0.25;
+
+      glColor4fv (color);
+      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
+
+      for (i = 0; i < 3; i++)
+        {
+          /* Orient to direction of corner. */
+          GLfloat x = t->p[i].x - c.x;
+          GLfloat y = t->p[i].y - c.y;
+          GLfloat z = t->p[i].z - c.z;
+
+          GLfloat smc = sr * y - cr * x;
+          GLfloat spc = cr * y + sr * x;
+
+          GLfloat st2 = t2 * scale / sqrt(x*x + y*y);
+          GLfloat slength = length * scale / sqrt(x*x + y*y + z*z);
+
+          GLfloat xt2 = spc * st2;
+          GLfloat yt2 = smc * st2;
+          GLfloat xlength = c.x - slength * smc;
+          GLfloat ylength = c.y + slength * spc;
+          GLfloat zlength = c.z + slength * z;
+
+          if (! wire)
+            glVertex3f (c.x - xt2, c.y - yt2, c.z);
+
+          glVertex3f (c.x + xt2, c.y + yt2, c.z);
+          if (wire)
+            glVertex3f (xlength + xt2, ylength + yt2, zlength);
+
+          if (! wire)
+            glVertex3f (xlength + xt2, ylength + yt2, zlength);
+
+          glVertex3f (xlength - xt2, ylength - yt2, zlength);
+
+          if (wire)
+            glVertex3f (c.x - xt2, c.y - yt2, c.z);
+
+          mi->polygon_count++;
+        }
+    }
+
+  glEnd();
+}
+
+
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_hexstrut (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);
+
+# ifdef HAVE_MOBILE    /* Keep it the same relative size when rotated. */
+  {
+    int o = (int) current_device_rotation();
+    if (o != 0 && o != 180 && o != -180)
+      glScalef (1/h, 1/h, 1/h);
+  }
+# endif
+
+  glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+ENTRYPOINT Bool
+hexstrut_handle_event (ModeInfo *mi, XEvent *event)
+{
+  hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
+
+  if (gltrackball_event_handler (event, bp->trackball,
+                                 MI_WIDTH (mi), MI_HEIGHT (mi),
+                                 &bp->button_down_p))
+    return True;
+  else if (event->xany.type == KeyPress)
+    {
+      KeySym keysym;
+      char c = 0;
+      XLookupString (&event->xkey, &c, 1, &keysym, 0);
+      if (c == ' ' || c == '\t')
+        {
+          bp->ncolors = 64;
+          make_smooth_colormap (0, 0, 0,
+                                bp->colors, &bp->ncolors,
+                                False, 0, False);
+          return True;
+        }
+    }
+
+  return False;
+}
+
+
+ENTRYPOINT void 
+init_hexstrut (ModeInfo *mi)
+{
+  hexstrut_configuration *bp;
+
+  if (!bps) {
+    bps = (hexstrut_configuration *)
+      calloc (MI_NUM_SCREENS(mi), sizeof (hexstrut_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_hexstrut (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+  {
+    double spin_speed   = 0.002;
+    double wander_speed = 0.003;
+    double spin_accel   = 1.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 (True);
+  }
+
+
+  /* Let's tilt the scene a little. */
+  gltrackball_start (bp->trackball, 500, 500, 1000, 1000);
+  gltrackball_track (bp->trackball,
+                     350 + (random() % 300),
+                     350 + (random() % 300),
+                     1000, 1000);
+
+
+  if (thickness < 0.05) thickness = 0.05;
+  if (thickness < 0.05) MI_IS_WIREFRAME(mi) = True;
+  if (thickness > 1.7) thickness = 1.7;
+  if (speed > 2) speed = 2;
+
+  bp->ncolors = 64;
+  bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
+  make_smooth_colormap (0, 0, 0,
+                        bp->colors, &bp->ncolors,
+                        False, 0, False);
+
+  make_plane (mi);
+}
+
+
+ENTRYPOINT void
+draw_hexstrut (ModeInfo *mi)
+{
+  hexstrut_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);
+
+  glDisable(GL_DEPTH_TEST);
+  glEnable(GL_NORMALIZE);
+  glDisable(GL_CULL_FACE);
+
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glPushMatrix ();
+
+  {
+    double x, y, z;
+    get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
+    glTranslatef((x - 0.5) * 6,
+                 (y - 0.5) * 6,
+                 (z - 0.5) * 12);
+
+    gltrackball_rotate (bp->trackball);
+
+    get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
+    glRotatef (z * 360, 0.0, 0.0, 1.0);
+  }
+
+  mi->polygon_count = 0;
+
+  glScalef (30, 30, 30);
+
+  if (! bp->button_down_p)
+    tick_triangles (mi);
+  draw_triangles (mi);
+
+  glPopMatrix ();
+
+  if (mi->fps_p) do_fps (mi);
+  glFinish();
+
+  glXSwapBuffers(dpy, window);
+}
+
+
+ENTRYPOINT void
+release_hexstrut (ModeInfo *mi)
+{
+  hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
+  while (bp->triangles)
+    {
+      triangle *t = bp->triangles->next;
+      free (bp->triangles);
+      bp->triangles = t;
+    }
+}
+
+XSCREENSAVER_MODULE ("Hexstrut", hexstrut)
+
+#endif /* USE_GL */