diff options
Diffstat (limited to 'hacks/glx/covid19.c')
-rw-r--r-- | hacks/glx/covid19.c | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/hacks/glx/covid19.c b/hacks/glx/covid19.c new file mode 100644 index 0000000..62d3c6e --- /dev/null +++ b/hacks/glx/covid19.c @@ -0,0 +1,656 @@ +/* covid19, Copyright (c) 2020 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. + * + * Created: Thursday, March 264th, 2020. + */ + +#define DEFAULTS "*delay: 30000 \n" \ + "*count: 60 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*membraneColor: #AAFFAA" "\n" \ + "*spikeColor: #DD0000" "\n" \ + "*mpColor: #8888FF" "\n" \ + "*epColor: #FF8888" "\n" \ + "*hesColor: #880088" "\n" \ + "*suppressRotationAnimation: True\n" \ + +# define release_ball 0 + +#include "xlockmore.h" +#include "colors.h" +#include "sphere.h" +#include "tube.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" + +#define SPIKE_FACES 12 +#define SMOOTH_SPIKES True +#define SPHERE_SLICES 64 +#define SPHERE_STACKS 32 +#define SPHERE_SLICES_2 16 +#define SPHERE_STACKS_2 8 + +#define SPIKE_FACESb 3 +#define SPHERE_SLICESb 10 +#define SPHERE_STACKSb 5 +#define SPHERE_SLICES_2b 5 +#define SPHERE_STACKS_2b 3 + + +typedef struct { GLfloat x, y, z; } XYZ; +typedef enum { MEMBRANE, SPIKE, M_PROTEIN, E_PROTEIN, HES } feature; +typedef enum { IN, DRAW, OUT } draw_mode; + +#undef RANDSIGN +#define RANDSIGN() ((random() & 1) ? 1 : -1) + +typedef struct { + XYZ pos; + GLfloat scale; + rotator *rot; + GLuint dlist; +} ball; + +typedef struct { + GLXContext *glx_context; + trackball_state *trackball; + Bool button_down_p; + draw_mode mode; + GLfloat tick; + GLuint ball_lists[20]; + int ball_polys; + int max_balls, count, ball_delta; + ball *balls; + GLfloat membrane_color[4]; + GLfloat spike_color[4]; + GLfloat mp_color[4]; + GLfloat ep_color[4]; + GLfloat hes_color[4]; +} ball_configuration; + +static ball_configuration *bps = NULL; + +static Bool do_spin; +static GLfloat speed; +static Bool do_wander; + +static XrmOptionDescRec opts[] = { + { "-spin", ".spin", XrmoptionNoArg, "True" }, + { "+spin", ".spin", XrmoptionNoArg, "False" }, + { "-wander", ".wander", XrmoptionNoArg, "True" }, + { "+wander", ".wander", XrmoptionNoArg, "False" }, + { "-speed", ".speed", 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}, +}; + +ENTRYPOINT ModeSpecOpt ball_opts = { + countof(opts), opts, countof(vars), vars, NULL}; + + +/* Window management, etc + */ +ENTRYPOINT void +reshape_ball (ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + int y = 0; + + if (width > height * 5) { /* tiny window: show middle */ + height = width * 9/16; + y = -height/2; + h = height / (GLfloat) width; + } + + glViewport (0, y, (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); + + { + GLfloat s = (MI_WIDTH(mi) < MI_HEIGHT(mi) + ? (MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi)) + : 1); + glScalef (s, s, s); + } + + glClear(GL_COLOR_BUFFER_BIT); +} + + +ENTRYPOINT Bool +ball_handle_event (ModeInfo *mi, XEvent *event) +{ + ball_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 (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event)) + { + bp->mode = OUT; + bp->tick = 1; + return True; + } + + return False; +} + + +static int +unit_spike (ModeInfo *mi, Bool lowrez) +{ + ball_configuration *bp = &bps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + GLfloat r = 0.2; + GLfloat s = 0.2; + int i; + glPushMatrix(); + + glColor4fv (bp->spike_color); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bp->spike_color); + + glScalef (s, s, s); + glTranslatef (0, -r, 0); + if (!lowrez) + glTranslatef (-r, 0, 0); + polys += tube (0, 0, 0, + 0, 1, 0, + r, 0, + (lowrez ? SPIKE_FACESb : SPIKE_FACES), + True, False, wire); + if (!lowrez) + glTranslatef (r*2, 0, 0); + if (! lowrez) + polys += tube (0, 0, 0, + 0, 1, 0, + r, 0, + (lowrez ? SPIKE_FACESb : SPIKE_FACES), + True, False, wire); + if (!lowrez) + glTranslatef (-r, 0, 0); + + glTranslatef (0, 1, 0); + r *= 2; + glScalef (r, r, r); + + for (i = 0; i < (lowrez ? 1 : 3); i++) + { + glPushMatrix(); + glRotatef (360.0 / 3 * i, 0, 1, 0); + if (!lowrez) + glTranslatef (r, 0, 0); + polys += unit_sphere ((lowrez ? SPHERE_STACKS_2b : SPHERE_STACKS_2), + (lowrez ? SPHERE_SLICES_2b : SPHERE_SLICES_2), + wire); + glPopMatrix(); + } + + glPopMatrix(); + return polys; +} + + +static int +unit_ball (ModeInfo *mi, Bool lowrez) +{ + ball_configuration *bp = &bps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + feature f; + + for (f = 0; f <= HES; f++) + { + switch (f) { + case MEMBRANE: + glColor4fv (bp->membrane_color); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bp->membrane_color); + polys += unit_sphere ((lowrez ? SPHERE_STACKSb : SPHERE_STACKS), + (lowrez ? SPHERE_SLICESb : SPHERE_SLICES), + wire); + break; + + case SPIKE: + { + GLfloat th0 = atan (0.5); /* lat division: 26.57 deg */ + GLfloat s = M_PI / 5; /* lon division: 72 deg */ + int i, j; + int n = (lowrez ? 8 : 10); + for (j = 0; j < n; j++) + for (i = 0; i < n; i++) + { + GLfloat th1 = s * i; + GLfloat a = th0; + GLfloat o = th1; + GLfloat x, y, z; + + a += (0.2 + frand (0.9)) * RANDSIGN(); + o += (0.2 + frand (0.9)) * RANDSIGN(); + + x = cos(a) * cos(o); + y = cos(a) * sin(o); + z = sin(a); + + glPushMatrix(); + + if (! (i & 1)) + { + glRotatef (180, 0, 1, 0); + glRotatef (180/5, 0, 0, 1); + } + + glTranslatef (x, y, z); + glRotatef (-atan2 (x, y) * (180/M_PI), 0, 0, 1); + glRotatef ( atan2 (z, sqrt(x*x + y*y)) * (180/M_PI), 1, 0, 0); + polys += unit_spike (mi, lowrez); + glPopMatrix(); + } + + glPushMatrix(); + glRotatef (90, 1, 0, 0); + glTranslatef (0, 1, 0); + polys += unit_spike (mi, lowrez); + + glTranslatef (0, -2, 0); + glRotatef (180, 1, 0, 0); + polys += unit_spike (mi, lowrez); + glPopMatrix(); + + } + break; + + default: + { + GLfloat s = 0.04; + int n = (lowrez ? 50 : 200); + int i; + GLfloat *c; + switch (f) { + case M_PROTEIN: c = bp->mp_color; break; + case E_PROTEIN: c = bp->ep_color; break; + case HES: c = bp->hes_color; break; + default: abort(); + } + + glColor4fv (c); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c); + + if (f == HES) + { + s *= 1.5; + n /= 8; + } + + for (i = 0; i < n; i++) + { + glPushMatrix(); + glRotatef (random() % 360, 1, 0, 0); + glRotatef (random() % 180, 0, 1, 0); + glTranslatef (1, 0, 0); + glRotatef (90, 0, 0, 1); + glScalef (s, s, s); + polys += unit_dome ((lowrez ? SPHERE_STACKS_2b : SPHERE_STACKS_2), + (lowrez ? SPHERE_SLICES_2b : SPHERE_SLICES_2), + wire); + glPopMatrix(); + } + } + break; + } + } + + return polys; +} + + +static void +parse_color (ModeInfo *mi, char *key, GLfloat color[4]) +{ + XColor xcolor; + char *string = get_string_resource (mi->dpy, key, "RobotColor"); + if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor)) + { + fprintf (stderr, "%s: unparsable color in %s: %s\n", progname, + key, string); + exit (1); + } + free (string); + + color[0] = xcolor.red / 65536.0; + color[1] = xcolor.green / 65536.0; + color[2] = xcolor.blue / 65536.0; + color[3] = 1; +} + + +static void +make_balls (ModeInfo *mi, int count) +{ + /* Distribute the balls into a rectangular grid that fills the window. + There may be some empty cells. N items in a W x H rectangle: + N = W * H + N = W * W * R + N/R = W*W + W = sqrt(N/R) + */ + ball_configuration *bp = &bps[MI_SCREEN(mi)]; + + GLfloat aspect = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi); + int nlines = sqrt (count / aspect) + 0.5; + int *cols = (int *) calloc (nlines, sizeof(*cols)); + int i, x, y, max = 0; + GLfloat scale, spacing; + Bool lowrez = (count > 40); + + if (bp->balls) + { + for (i = 0; i < bp->count; i++) + free_rotator (bp->balls[i].rot); + free (bp->balls); + } + + bp->count = count; + bp->balls = (ball *) calloc (sizeof (*bp->balls), count); + + for (i = 0; i < count; i++) + { + cols[i % nlines]++; + if (cols[i % nlines] > max) max = cols[i % nlines]; + } + + /* That gave us, e.g. 7777666. Redistribute to 6767767. */ + for (i = 0; i < nlines / 2; i += 2) + { + int j = nlines-i-1; + int swap = cols[i]; + cols[i] = cols[j]; + cols[j] = swap; + } + + scale = 1.0 / nlines; /* Scale for height */ + if (scale * max > aspect) /* Shrink if overshot width */ + scale *= aspect / (scale * max); + + scale *= 0.9; /* Add padding */ + spacing = scale * 4; + + if (count == 1) spacing = 0; + + i = 0; + for (y = 0; y < nlines; y++) + for (x = 0; x < cols[y]; x++) + { + ball *v = &bp->balls[i]; + double spin_speed = 1.0 * speed; + double wander_speed = 0.04 * speed; + double spin_accel = 1.0; + int n = countof (bp->ball_lists) / 2; + + v->scale = scale; + v->pos.x = spacing * (x - cols[y] / 2.0) + spacing/2; + v->pos.y = spacing * (y - nlines / 2.0) + spacing/2; + v->pos.z = 0; + v->dlist = bp->ball_lists [(random() % n) + (lowrez ? n : 0)]; + v->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, + True); + i++; + } + free (cols); +} + + +ENTRYPOINT void +init_ball (ModeInfo *mi) +{ + ball_configuration *bp = &bps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + int i; + + MI_INIT (mi, bps); + bp = &bps[MI_SCREEN(mi)]; + + bp->glx_context = init_GL(mi); + + reshape_ball (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); + } + + parse_color (mi, "membraneColor", bp->membrane_color); + parse_color (mi, "spikeColor", bp->spike_color); + parse_color (mi, "mpColor", bp->mp_color); + parse_color (mi, "epColor", bp->ep_color); + parse_color (mi, "hesColor", bp->hes_color); + + for (i = 0; i < countof(bp->ball_lists); i++) + { + Bool lowrez = (i > countof(bp->ball_lists) / 2); + bp->ball_lists[i] = glGenLists (1); + glNewList (bp->ball_lists[i], GL_COMPILE); + bp->ball_polys = unit_ball (mi, lowrez); + glEndList (); + } + + bp->ball_delta = 1; + bp->max_balls = MI_COUNT(mi); + if (bp->max_balls < 1) bp->max_balls = 1; + bp->count = (bp->max_balls > 10 ? 1 + random() % 5 : + bp->max_balls > 5 ? 1 + random() % 3 : 1); + make_balls (mi, bp->count); + + bp->trackball = gltrackball_init (True); +} + + +ENTRYPOINT void +draw_ball (ModeInfo *mi) +{ + ball_configuration *bp = &bps[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + GLfloat s = 1; + int i; + + static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0}; + static const GLfloat bshiny = 128.0; + + 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 (4, 4, 4); + + gltrackball_rotate (bp->trackball); + + mi->polygon_count = 0; + + glMaterialfv (GL_FRONT, GL_SPECULAR, bspec); + glMateriali (GL_FRONT, GL_SHININESS, bshiny); + + switch (bp->mode) { + case DRAW: + bp->tick -= 1/30.0/5; /* No more often than 5 sec */ + if (bp->tick <= 0) + { + bp->tick = 1; + if (! (random() % 20)) + { + bp->mode = OUT; + bp->tick = 1; + } + } + s = 1; + break; + case IN: + bp->tick += 1/12.0; + if (bp->tick >= 1) + { + bp->tick = 1; + bp->mode = DRAW; + } + s = bp->tick; + break; + case OUT: + bp->tick -= 1/12.0; + s = bp->tick; + if (bp->tick <= 0) + { + int c2; + int n; + bp->tick = 0; + bp->mode = IN; + + n = (bp->count < 5 ? 2 : + bp->count < 20 ? 5 : 20); + c2 = bp->count + (1 + (random() % n)) * bp->ball_delta; + if (c2 < 1) + { + c2 = 1; + bp->ball_delta = 1; + } + else if (c2 > bp->max_balls) + { + c2 = bp->max_balls; + bp->ball_delta = -1; + } + + make_balls (mi, c2); + s = 0; + } + break; + default: + abort(); + } + + if (s > 0) + for (i = 0; i < bp->count; i++) + { + ball *v = &bp->balls[i]; + double x, y, z; + glPushMatrix(); + glTranslatef (v->pos.x, v->pos.y, v->pos.z); + glScalef (v->scale, v->scale, v->scale); + + get_position (v->rot, &x, &y, &z, !bp->button_down_p); + glTranslatef((x - 0.5) * 2, + (y - 0.5) * 2, + (z - 0.5) * 8 * (bp->count > 8 ? 3 : 1)); + get_rotation (v->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); + + glScalef (s, s, s); + glCallList (v->dlist); + mi->polygon_count += bp->ball_polys; + glPopMatrix (); + } + glPopMatrix (); + + mi->recursion_depth = bp->count; + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + + +ENTRYPOINT void +free_ball (ModeInfo *mi) +{ + ball_configuration *bp = &bps[MI_SCREEN(mi)]; + int i; + if (!bp->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); + for (i = 0; i < bp->count; i++) + free_rotator (bp->balls[i].rot); + free (bp->balls); + if (bp->trackball) gltrackball_free (bp->trackball); + for (i = 0; i < countof(bp->ball_lists); i++) + if (glIsList(bp->ball_lists[i])) glDeleteLists(bp->ball_lists[i], 1); +} + +#ifndef HAVE_IPHONE +XSCREENSAVER_MODULE_2 ("COVID19", covid19, ball) +#else +XSCREENSAVER_MODULE_2 ("Co____9", co____9, ball) + + /* App Store Connect Resolution Center: App Review + + Binary Rejected + + Guideline 1.1 - Safety - Objectionable Content We found that your app + includes content or concepts that some users may find upsetting, + + We found that your app includes content or concepts that some users may + find upsetting, offensive, or otherwise objectionable. + + Specifically, your entertainment or gaming app inappropriately references + the C____-__ p__d__ic in the metadata or binary. Entertainment or gaming + apps that directly or indirectly reference the C____-__ p__d__ic in any + way are not appropriate for the App Store. + + */ +#endif + + +#endif /* USE_GL */ |