summaryrefslogtreecommitdiffstats
path: root/hacks/glx/unknownpleasures.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/unknownpleasures.c')
-rw-r--r--hacks/glx/unknownpleasures.c486
1 files changed, 486 insertions, 0 deletions
diff --git a/hacks/glx/unknownpleasures.c b/hacks/glx/unknownpleasures.c
new file mode 100644
index 0000000..9802566
--- /dev/null
+++ b/hacks/glx/unknownpleasures.c
@@ -0,0 +1,486 @@
+/* unknownpleasures, Copyright (c) 2013-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
+ * 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.
+ *
+ * Translated from Mathematica code by Archery:
+ * http://intothecontinuum.tumblr.com/post/27443100682/in-july-1967-astronomers-at-the-cavendish
+ *
+ * Interestingly, the original image is copyright-free:
+ * http://adamcap.com/2011/05/19/history-of-joy-division-unknown-pleasures-album-art/
+ * https://en.wikipedia.org/wiki/Unknown_Pleasures
+ *
+ * TODO:
+ *
+ * - Performance is not great. Spending half our time in compute_line()
+ * and half our time in glEnd(). It's a vast number of cos/pow calls,
+ * and a vast number of polygons. I'm not sure how much could be cached.
+ *
+ * - There's too low periodicity vertically on the screen.
+ *
+ * - There's too low periodicity in time.
+ *
+ * - Could take advantage of time periodicity for caching: just save every
+ * poly for an N second loop. That would be a huge amount of RAM though.
+ *
+ * - At low resolutions, GL_POLYGON_OFFSET_FILL seems to work poorly
+ * and the lines get blocky.
+ */
+
+
+#define DEFAULTS "*delay: 30000 \n" \
+ "*count: 80 \n" \
+ "*resolution: 100 \n" \
+ "*ortho: True \n" \
+ "*showFPS: False \n" \
+ "*wireframe: False \n" \
+ "*geometry: =800x800" "\n" \
+
+# define free_unk 0
+# define release_unk 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "colors.h"
+#include "gltrackball.h"
+#include <ctype.h>
+
+#ifdef USE_GL /* whole file */
+
+#define DEF_SPEED "1.0"
+
+
+typedef struct {
+ GLXContext *glx_context;
+ trackball_state *trackball;
+ Bool button_down_p;
+ Bool orthop;
+ GLfloat resolution;
+ int count;
+ GLfloat t;
+} unk_configuration;
+
+static unk_configuration *bps = NULL;
+
+static GLfloat speed;
+
+static XrmOptionDescRec opts[] = {
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-resolution", ".resolution", XrmoptionSepArg, 0 },
+ { "-ortho", ".ortho", XrmoptionNoArg, "True" },
+ { "-no-ortho", ".ortho", XrmoptionNoArg, "False" },
+};
+
+static argtype vars[] = {
+ {&speed, "speed", "Speed", DEF_SPEED, t_Float},
+};
+
+ENTRYPOINT ModeSpecOpt unk_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+
+
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_unk (ModeInfo *mi, int width, int height)
+{
+ unk_configuration *bp = &bps[MI_SCREEN(mi)];
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+ int y = 0;
+
+ if (width > height * 5) { /* tiny window: show middle */
+ height = width*1.5;
+ y = -height/2;
+ h = height / (GLfloat) width;
+ }
+
+ glViewport (0, y, (GLint) width, (GLint) height);
+
+ if (bp->orthop)
+ {
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (1.0, 1/h, 690, 710);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0, 0, 700,
+ 0, 0, 0,
+ 0, 1, 0);
+ if (width < height)
+ glScalef (1/h, 1/h, 1);
+ glTranslatef (0, -0.5, 0);
+ }
+ else
+ {
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (30.0, 1/h, 20.0, 40.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0.0, 0.0, 30.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
+ if (width < height)
+ glScalef (1/h, 1/h, 1);
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+ENTRYPOINT Bool
+unk_handle_event (ModeInfo *mi, XEvent *event)
+{
+ unk_configuration *bp = &bps[MI_SCREEN(mi)];
+
+ if (event->xany.type == ButtonPress &&
+ (event->xbutton.button == Button4 ||
+ event->xbutton.button == Button5 ||
+ event->xbutton.button == Button6 ||
+ event->xbutton.button == Button7))
+ {
+ int b = event->xbutton.button;
+ int speed = 1;
+ if (b == Button6 || b == Button7)
+ speed *= 3;
+ if (bp->orthop)
+ switch (b) { /* swap axes */
+ case Button4: b = Button6; break;
+ case Button5: b = Button7; break;
+ case Button6: b = Button4; break;
+ case Button7: b = Button5; break;
+ }
+ gltrackball_mousewheel (bp->trackball, b, speed, !!event->xbutton.state);
+ return True;
+ }
+ else if (gltrackball_event_handler (event, bp->trackball,
+ MI_WIDTH (mi), MI_HEIGHT (mi),
+ &bp->button_down_p))
+ return True;
+ else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
+ {
+ bp->orthop = !bp->orthop;
+ reshape_unk (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+ return True;
+ }
+
+ return False;
+}
+
+
+
+ENTRYPOINT void
+init_unk (ModeInfo *mi)
+{
+ unk_configuration *bp;
+
+ MI_INIT (mi, bps);
+
+ bp = &bps[MI_SCREEN(mi)];
+
+ bp->glx_context = init_GL(mi);
+
+ bp->orthop = get_boolean_resource (MI_DISPLAY (mi), "ortho", "Ortho");
+ bp->resolution = get_float_resource (MI_DISPLAY (mi),
+ "resolution", "Resolution");
+ if (bp->resolution < 1) bp->resolution = 1;
+ if (bp->resolution > 300) bp->resolution = 300;
+
+ reshape_unk (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+ bp->count = MI_COUNT(mi);
+ if (bp->count < 1) bp->count = 1;
+
+ bp->trackball = gltrackball_init (False);
+
+ if (MI_COUNT(mi) < 1) MI_COUNT(mi) = 1;
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+
+
+static double
+R (double f)
+{
+ /* A simple, fast, deterministic PRNG.
+ ya_rand_init() is too slow for this, and the stream of numbers here
+ doesn't have to be high quality.
+ */
+#if 1
+ int seed0 = 1613287;
+
+# else
+ /* Kluge to let me pick a good seed factor by trial and error... */
+ static int seed0 = 0;
+ static int count = 0;
+ if (count++ > 150000000) seed0 = 0, count=0;
+ if (! seed0)
+ {
+ seed0 = (random() & 0xFFFFF);
+ fprintf(stderr, "seed0 = %8x %d\n", seed0, seed0);
+ }
+# endif
+
+ long seed = seed0 * f;
+ seed = 48271 * (seed % 44488) - 3399 * (seed / 44488);
+ f = (double) seed / 0x7FFFFFFF;
+
+ return f;
+}
+
+
+static void
+compute_line (ModeInfo *mi,
+ int xmin, int xmax, GLfloat xinc,
+ GLfloat res_scale,
+ int y,
+ GLfloat *points)
+{
+ unk_configuration *bp = &bps[MI_SCREEN(mi)];
+
+ GLfloat fx;
+ int lastx = -999999;
+
+ /* Compute the points on the line */
+
+ for (fx = xmin; fx < xmax; fx += xinc)
+ {
+ int x = fx;
+ int n;
+ GLfloat hsum = 0, vsum = 0;
+
+ if (x == lastx) continue;
+ lastx = x;
+
+ for (n = 1; n <= 30; n++)
+ {
+ /* This sum affects crinkliness of the lines */
+ hsum += (10 *
+ sin (2 * M_PI
+ * R (4 * y)
+ + bp->t
+ + R (2 * n * y)
+ * 2 * M_PI)
+ * exp (-pow ((.3 * (x / res_scale) + 30
+ - 1 * 100 * R (2 * n * y)),
+ 2)
+ / 20.0));
+ }
+
+ for (n = 1; n <= 4; n++)
+ {
+ /* This sum affects amplitude of the peaks */
+ vsum += (3 * (1 + R (3 * n * y))
+ * fabs (sin (bp->t + R (n * y)
+ * 2 * M_PI))
+ * exp (-pow (((x / res_scale) - 1 * 100 * R (n * y)),
+ 2)
+ / 20.0));
+ }
+
+ /* Scale of this affects amplitude of the flat parts */
+ points[x - xmin] = (0.2 * sin (2 * M_PI * R (6 * y) + hsum) + vsum);
+ }
+
+}
+
+
+ENTRYPOINT void
+draw_unk (ModeInfo *mi)
+{
+ unk_configuration *bp = &bps[MI_SCREEN(mi)];
+ Display *dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ int wire = MI_IS_WIREFRAME(mi);
+ GLfloat aspect = 1.5;
+
+ GLfloat res_scale = 4;
+ int xmin = -50 * res_scale;
+ int xmax = 150 * res_scale;
+ GLfloat xinc = 100.0 / (bp->resolution / res_scale);
+ int ymin = 1;
+ int ytop = MI_COUNT(mi);
+ int yinc = 1;
+ int y;
+ GLfloat *points;
+
+ if (xinc < 0.25) xinc = 0.25;
+
+ if (!bp->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ mi->polygon_count = 0;
+
+ glShadeModel (GL_FLAT);
+ glEnable (GL_DEPTH_TEST);
+ glDisable (GL_CULL_FACE);
+
+ glPushMatrix ();
+
+ glRotatef(current_device_rotation(), 0, 0, 1);
+
+ gltrackball_rotate (bp->trackball);
+ glScalef (10, 10, 10);
+
+ glRotatef (-45, 1, 0, 0);
+ glTranslatef (-0.5, -0.5, 0);
+ if (bp->orthop)
+ glTranslatef (0, 0.05, 0);
+ else
+ glTranslatef (0, 0.15, 0);
+
+ points = (GLfloat *) malloc (sizeof(*points) * (xmax - xmin));
+
+ if (!bp->button_down_p)
+ {
+ double s = 6.3 / 19 / 3;
+# if 1
+ bp->t -= s * speed;
+ if (bp->t <= 0)
+ bp->t = s * 18 * 3;
+# else
+ bp->t += s;
+# endif
+ }
+
+ glLineWidth (2);
+
+ /* Lower the resolution to get a decent frame rate on iPhone 4s */
+ if (mi->xgwa.width <= 640 || mi->xgwa.height <= 640)
+ {
+ ytop *= 0.6;
+ xinc *= 3;
+ }
+
+# ifdef HAVE_MOBILE
+ /* Lower it even further for iPhone 3 */
+ if (mi->xgwa.width <= 480 || mi->xgwa.height <= 480)
+ {
+ ytop *= 0.8;
+ xinc *= 1.2;
+ }
+
+ /* Performance just sucks on iPad 3, even with a very high xinc. WTF? */
+/*
+ if (mi->xgwa.width >= 2048 || mi->xgwa.height >= 2048)
+ xinc *= 2;
+*/
+
+# endif /* HAVE_MOBILE */
+
+
+ /* Make the image fill the screen a little more fully */
+ if (mi->xgwa.width <= 640 || mi->xgwa.height <= 640)
+ {
+ glScalef (1.2, 1.2, 1.2);
+ glTranslatef (-0.08, 0, 0);
+ }
+
+ if (mi->xgwa.width <= 480 || mi->xgwa.height <= 480)
+ glLineWidth (1);
+
+
+ if (wire)
+ xinc *= 1.3;
+
+ /* Draw back mask */
+ {
+ GLfloat s = 0.99;
+ glDisable (GL_POLYGON_OFFSET_FILL);
+ glColor3f (0, 0, 0);
+ glPushMatrix();
+ glTranslatef (0, (1-aspect)/2, -0.005);
+ glScalef (1, aspect, 1);
+ glTranslatef (0.5, 0.5, 0);
+ glScalef (s, s, 1);
+ glBegin (GL_QUADS);
+ glVertex3f (-0.5, -0.5, 0);
+ glVertex3f ( 0.5, -0.5, 0);
+ glVertex3f ( 0.5, 0.5, 0);
+ glVertex3f (-0.5, 0.5, 0);
+ glEnd();
+ glPopMatrix();
+ }
+
+ if (! wire)
+ {
+ glEnable (GL_LINE_SMOOTH);
+ glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+
+ /* So the masking quads don't interfere with the lines */
+ glEnable (GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset (1.0, 1.0);
+ }
+
+ for (y = ymin; y <= ytop; y += yinc)
+ {
+ /* Compute all the verts on the line */
+ compute_line (mi, xmin, xmax, xinc, res_scale, y, points);
+
+ /* Draw the line segments; then draw the black shielding quads. */
+ {
+ GLfloat yy = y / (GLfloat) ytop;
+ int linesp;
+
+ yy = (yy * aspect) - ((aspect - 1) / 2);
+
+ for (linesp = 0; linesp <= 1; linesp++)
+ {
+ GLfloat fx;
+ int lastx = -999999;
+
+ GLfloat c = (linesp || wire ? 1 : 0);
+ glColor3f (c, c, c);
+
+ glBegin (linesp
+ ? GL_LINE_STRIP
+ : wire ? GL_LINES : GL_QUAD_STRIP);
+ lastx = -999999;
+ for (fx = xmin; fx < xmax; fx += xinc)
+ {
+ int x = fx;
+ GLfloat xx = (x - xmin) / (GLfloat) (xmax - xmin);
+ GLfloat zz = points [x - xmin];
+
+ if (x == lastx) continue;
+ lastx = x;
+
+ zz /= 80;
+ glVertex3f (xx, yy, zz);
+ if (! linesp)
+ glVertex3f (xx, yy, 0);
+ mi->polygon_count++;
+ }
+ glEnd ();
+ }
+ }
+ }
+
+ free (points);
+
+ glPopMatrix ();
+
+ if (mi->fps_p) do_fps (mi);
+ glFinish();
+
+ glXSwapBuffers(dpy, window);
+}
+
+XSCREENSAVER_MODULE_2 ("UnknownPleasures", unknownpleasures, unk)
+
+#endif /* USE_GL */