X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Funknownpleasures.c;fp=hacks%2Fglx%2Funknownpleasures.c;h=b59cff68a3ea7de7278c2151b4826f737cb1aa96;hp=0000000000000000000000000000000000000000;hb=4ade52359b6eba3621566dac79793a33aa4c915f;hpb=ff35d056d723c9a5ffe728dbba5f1c25e141be04 diff --git a/hacks/glx/unknownpleasures.c b/hacks/glx/unknownpleasures.c new file mode 100644 index 00000000..b59cff68 --- /dev/null +++ b/hacks/glx/unknownpleasures.c @@ -0,0 +1,512 @@ +/* unknownpleasures, Copyright (c) 2013 Jamie Zawinski + * + * 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/ + * http://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 refresh_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 + +#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; + + glViewport (0, 0, (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 == 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)) + { + 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 (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 == ' ') + { + bp->orthop = !bp->orthop; + reshape_unk (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + return True; + } + return False; + } + + return False; +} + + + +ENTRYPOINT void +init_unk (ModeInfo *mi) +{ + unk_configuration *bp; + + if (!bps) { + bps = (unk_configuration *) + calloc (MI_NUM_SCREENS(mi), sizeof (unk_configuration)); + if (!bps) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(1); + } + } + + 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 (); + + 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 USE_IPHONE + /* 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 /* USE_IPHONE */ + + + /* 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 */