X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Frazzledazzle.c;fp=hacks%2Fglx%2Frazzledazzle.c;h=a3c8f98f235feb645c675cbeefdd8efda210f487;hp=0000000000000000000000000000000000000000;hb=78add6e627ee5f10e1fa6f3852602ea5066eee5a;hpb=39809ded547bdbb08207d3e514950425215b4410 diff --git a/hacks/glx/razzledazzle.c b/hacks/glx/razzledazzle.c new file mode 100644 index 00000000..a3c8f98f --- /dev/null +++ b/hacks/glx/razzledazzle.c @@ -0,0 +1,725 @@ +/* razzledazzle, Copyright (c) 2018 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. + */ + +#define DEFAULTS "*delay: 30000 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*ncolors: 2 \n" \ + "*suppressRotationAnimation: True\n" \ + +# define release_dazzle 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#include "xlockmore.h" +#include "colors.h" +#include "normals.h" +#include "gllist.h" +#include + +#ifdef USE_GL /* whole file */ + +#define DEF_SPEED "1.0" +#define DEF_DENSITY "5.0" +#define DEF_THICKNESS "0.1" +#define DEF_MODE "Random" + +#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) +#undef RANDSIGN +#define RANDSIGN() ((random() & 1) ? 1 : -1) + +extern const struct gllist + *ships_ship1, *ships_ship2, *ships_ship3, *ships_ship4, + *ships_ship5, *ships_ship6, *ships_ship7, *ships_ship8; + +static const struct gllist * const *all_ships[] = { + &ships_ship1, &ships_ship2, &ships_ship3, &ships_ship4, + &ships_ship5, &ships_ship6, &ships_ship7, &ships_ship8, +}; + + +typedef enum { LEFT, RIGHT, UP, DOWN } direction; + +typedef struct node node; + +struct node { + long gx, gy; + GLfloat x, y; + GLfloat dx, dy; + int nstripes; + Bool horiz_p; + Bool drawn_p; + GLfloat color1[4], color2[4]; +}; + +typedef struct { + GLXContext *glx_context; + Bool button_down_p; + GLfloat xoff, yoff, dx, dy; + node *nodes; + node *dragging; + int drag_x, drag_y; + int ncolors; + XColor *colors; + GLuint *dlists; + enum { SHIPS, FLAT, RANDOM } mode; + int which_ship; + long frames; +} dazzle_configuration; + +static dazzle_configuration *bps = NULL; + +static GLfloat speed, thickness, density; +static char *mode_arg; + +static XrmOptionDescRec opts[] = { + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-thickness", ".thickness", XrmoptionSepArg, 0 }, + { "-density", ".density", XrmoptionSepArg, 0 }, + { "-mode", ".mode", XrmoptionSepArg, 0 }, + { "-ships", ".mode", XrmoptionNoArg, "ships" }, + { "-flat", ".mode", XrmoptionNoArg, "flat" }, +}; + +static argtype vars[] = { + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float}, + {&density, "density", "Density", DEF_DENSITY, t_Float}, + {&mode_arg, "mode", "Mode", DEF_MODE, t_String}, +}; + +ENTRYPOINT ModeSpecOpt dazzle_opts = {countof(opts), opts, countof(vars), vars, NULL}; + + + +static void +draw_grid (ModeInfo *mi, int gx, int gy) +{ + dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; + Bool wire = MI_IS_WIREFRAME(mi); + long x, y; + long wh = density * 2; + + if (wire) + glColor3f (1, 1, 1); + + if (!wire) + glBegin (GL_QUADS); + + for (y = 0; y < wh; y++) + for (x = 0; x < wh; x++) + { + node *n0 = &bp->nodes[(y % wh) * wh + (x % wh)]; + node *n1 = &bp->nodes[(y % wh) * wh + ((x+1) % wh)]; + node *n2 = &bp->nodes[((y+1) % wh) * wh + ((x+1) % wh)]; + node *n3 = &bp->nodes[((y+1) % wh) * wh + (x % wh)]; + int nstripes, i; + Bool horiz_p, visible; + GLfloat x0, y0, x1, y1, x2, y2, x3, y3; + GLfloat xoff = (x < wh-1 ? 0 : wh); + GLfloat yoff = (y < wh-1 ? 0 : wh); + GLfloat bx = fmod ((double) bp->xoff, 2.0); + GLfloat by = fmod ((double) bp->yoff, 2.0); + + bx += gx*2; + by += gy*2; + + if (wire) + { + GLfloat a; + glColor3f (0, 0, 1); + glBegin (GL_LINE_LOOP); + for (a = 0; a < 360; a += 10) + glVertex3f ((n0->x / density-1) + 0.05 * cos(a * M_PI/180) + bx, + (n0->y / density-1) + 0.05 * sin(a * M_PI/180) + by, + 0); + glEnd(); + } + + x0 = n0->x / density - 1 + bx; + y0 = n0->y / density - 1 + by; + + x1 = (n1->x+xoff) / density - 1 + bx; + y1 = n1->y / density - 1 + by; + x2 = (n2->x+xoff) / density - 1 + bx; + y2 = (n2->y+yoff) / density - 1 + by; + x3 = n3->x / density - 1 + bx; + y3 = (n3->y+yoff) / density - 1 + by; + + if (wire) + { + if (gx == 0 && gy == 0) + { + glLineWidth (4); + glColor3f(1, 0, 0); + } + else + glColor3f(0.5, 0, 0.5); + if (wire) glBegin (GL_LINE_LOOP); + glVertex3f (x0, y0, 0); + glVertex3f (x1, y1, 0); + glVertex3f (x2, y2, 0); + glVertex3f (x3, y3, 0); + mi->polygon_count++; + if (wire) glEnd(); + glLineWidth (1); + } + + /* This isn't quite right: just because all corners are off screen + doesn't mean the quad isn't visible. We need to intersect the + edges with the screen rectangle. + */ + { + GLfloat max = 0.75; + visible = ((x0 >= -max && y0 >= -max && x0 <= max && y0 <= max) || + (x1 >= -max && y1 >= -max && x1 <= max && y1 <= max) || + (x2 >= -max && y2 >= -max && x2 <= max && y2 <= max) || + (x3 >= -max && y3 >= -max && x3 <= max && y3 <= max)); + } + + if (!visible) continue; + + if (visible) + n0->drawn_p = True; + + nstripes = n0->nstripes; + horiz_p = n0->horiz_p; + + for (i = 0; i < nstripes; i++) + { + GLfloat ss = (GLfloat) i / nstripes; + GLfloat ss1 = (GLfloat) (i+1) / nstripes; + if (i & 1) + glColor4fv (n0->color1); + else if (wire) + continue; + else + glColor4fv (n0->color2); + + if (horiz_p) + { + x0 = n0->x + (n3->x - n0->x) * ss; + y0 = n0->y + ((n3->y+yoff) - n0->y) * ss; + x1 = (n1->x+xoff) + ((n2->x+xoff) - (n1->x+xoff)) * ss; + y1 = n1->y + ((n2->y+yoff) - n1->y) * ss; + + x2 = (n1->x+xoff) + ((n2->x+xoff) - (n1->x+xoff)) * ss1; + y2 = n1->y + ((n2->y+yoff) - n1->y) * ss1; + x3 = n0->x + (n3->x - n0->x) * ss1; + y3 = n0->y + ((n3->y+yoff) - n0->y) * ss1; + } + else + { + x0 = n0->x + ((n1->x+xoff) - n0->x) * ss; + y0 = n0->y + (n1->y - n0->y) * ss; + x1 = n3->x + ((n2->x+xoff) - n3->x) * ss; + y1 = (n3->y+yoff) + ((n2->y+yoff) - (n3->y+yoff)) * ss; + + x2 = n3->x + ((n2->x+xoff) - n3->x) * ss1; + y2 = (n3->y+yoff) + ((n2->y+yoff) - (n3->y+yoff)) * ss1; + x3 = n0->x + ((n1->x+xoff) - n0->x) * ss1; + y3 = n0->y + (n1->y - n0->y) * ss1; + } + + if (wire) glBegin (GL_LINES); + glVertex3f (x0 / density - 1 + bx, y0 / density - 1 + by, 0); + glVertex3f (x1 / density - 1 + bx, y1 / density - 1 + by, 0); + glVertex3f (x2 / density - 1 + bx, y2 / density - 1 + by, 0); + glVertex3f (x3 / density - 1 + bx, y3 / density - 1 + by, 0); + mi->polygon_count++; + if (wire) glEnd(); + } + } + + if (!wire) + glEnd(); +} + + +static void +move_grid (ModeInfo *mi) +{ + dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; + long x, y; + long wh = density * 2; + Bool wire = MI_IS_WIREFRAME(mi); + GLfloat max = 1.0 / density * 3; + + if (bp->button_down_p) + return; + + bp->xoff += bp->dx; + bp->yoff += bp->dy; + + if (! (random() % 50)) + { + bp->dx += frand(0.0002) * RANDSIGN() * speed; + bp->dy += frand(0.0002) * RANDSIGN() * speed; + } + + if (bp->dx > 0.003 * speed) bp->dx = 0.003 * speed; + if (bp->dy > 0.003 * speed) bp->dy = 0.003 * speed; + + for (y = 0; y < wh; y++) + for (x = 0; x < wh; x++) + { + node *n = &bp->nodes[y * wh + x]; + GLfloat x2 = n->x + n->dx; + GLfloat y2 = n->y + n->dy; + + if (x2 < n->gx + max && x2 >= n->gx - max && + y2 < n->gy + max && y2 >= n->gy - max) + { + n->x = x2; + n->y = y2; + } + + if (! (random() % 50)) + { + n->dx += frand(0.0005) * RANDSIGN() * speed; + n->dy += frand(0.0005) * RANDSIGN() * speed; + } + + /* If this quad was not drawn, it's ok to re-randomize stripes, */ + if (! n->drawn_p) + { + int i = random() % bp->ncolors; + int j = (i + bp->ncolors / 2) % bp->ncolors; + GLfloat cscale = 0.3; + + n->color1[0] = bp->colors[i].red / 65536.0; + n->color1[1] = bp->colors[i].green / 65536.0; + n->color1[2] = bp->colors[i].blue / 65536.0; + n->color1[3] = 1.0; + + n->color2[0] = bp->colors[j].red / 65536.0; + n->color2[1] = bp->colors[j].green / 65536.0; + n->color2[2] = bp->colors[j].blue / 65536.0; + n->color2[3] = 1.0; + + if (! wire) + { + n->color1[0] = cscale * n->color1[0] + 1 - cscale; + n->color1[1] = cscale * n->color1[1] + 1 - cscale; + n->color1[2] = cscale * n->color1[2] + 1 - cscale; + n->color2[0] = cscale * n->color2[0]; + n->color2[1] = cscale * n->color2[1]; + n->color2[2] = cscale * n->color2[2]; + } + + n->horiz_p = random() & 1; + n->nstripes = 2 + (int) (BELLRAND(1.0 / thickness)); + } + n->drawn_p = False; + } +} + + +/* Window management, etc + */ +ENTRYPOINT void +reshape_dazzle (ModeInfo *mi, int width, int height) +{ + glViewport (0, 0, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho (0, 1, 1, 0, -1, 1); + + if (width > height * 5) { /* tiny window: show middle */ + GLfloat s = (GLfloat)height/width; + glOrtho (0, 1, 0.5-s, 0.5+s, -1, 1); + } + +# ifdef USE_IPHONE /* So much WTF */ + { + int rot = current_device_rotation(); + + glTranslatef (0.5, 0.5, 0); + + if (rot == 180 || rot == -180) { + glTranslatef (1, 1, 0); + } else if (rot == 90 || rot == -270) { + glRotatef (180, 0, 0, 1); + glTranslatef (0, 1, 0); + } else if (rot == -90 || rot == 270) { + glRotatef (180, 0, 0, 1); + glTranslatef (1, 0, 0); + } + + glTranslatef(-0.5, -0.5, 0); + } +# endif + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glClear(GL_COLOR_BUFFER_BIT); +} + + +static void +dazzle_randomize (ModeInfo *mi) +{ + dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; + long x, y; + long wh = density * 2; + + bp->ncolors = MI_NCOLORS(mi) - 1; + if (bp->ncolors < 1) bp->ncolors = 1; + if (bp->colors) free (bp->colors); + bp->colors = (XColor *) calloc (bp->ncolors, sizeof(XColor)); + if (bp->ncolors < 3) + make_random_colormap (0, 0, 0, bp->colors, &bp->ncolors, + True, False, 0, False); + else + make_smooth_colormap (0, 0, 0, + bp->colors, &bp->ncolors, + False, False, False); + if (bp->ncolors < 1) abort(); + + bp->dragging = 0; + if (bp->nodes) free (bp->nodes); + + bp->nodes = (node *) calloc (wh * wh, sizeof (node)); + for (y = 0; y < wh; y++) + for (x = 0; x < wh; x++) + { + node *n = &bp->nodes[wh * y + x]; + n->gx = n->x = x; + n->gy = n->y = y; + } + + bp->dx = bp->dy = 0; + bp->xoff = bp->yoff = 0; + for (x = 0; x < 1000; x++) + move_grid (mi); + + bp->dx = frand(0.0005) * RANDSIGN() * speed; + bp->dy = frand(0.0005) * RANDSIGN() * speed; + + if (bp->mode == SHIPS || bp->mode == RANDOM) + { + bp->which_ship = random() % countof(all_ships); + if (bp->mode == RANDOM && !(random() % 3)) + bp->which_ship = -1; + } + + if (bp->which_ship != -1) + { + bp->dx /= 10; + bp->dy /= 10; + } +} + + +ENTRYPOINT Bool +dazzle_handle_event (ModeInfo *mi, XEvent *event) +{ + dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; + Bool wire = MI_IS_WIREFRAME(mi); + GLfloat bx = fmod ((double) bp->xoff, 2.0); + GLfloat by = fmod ((double) bp->yoff, 2.0); + long wh = density * 2; + + if (event->xany.type == ButtonPress) + { + GLfloat x = (GLfloat) event->xbutton.x / MI_WIDTH (mi) - 0.5; + GLfloat y = (GLfloat) event->xbutton.y / MI_HEIGHT (mi) - 0.5; + node *nn = 0; + int xoff = 0, yoff = 0; + GLfloat d2 = 999999; + long x0, y0, x1, y1; + + if (wire) x /= 0.2, y /= 0.2; + + for (y0 = -1; y0 <= 1; y0++) + for (x0 = -1; x0 <= 1; x0++) + for (y1 = 0; y1 < wh; y1++) + for (x1 = 0; x1 < wh; x1++) + { + node *n0 = &bp->nodes[(y1 % wh) * wh + (x1 % wh)]; + double dist2; + GLfloat x2 = n0->x / density - 1 + bx + x0*2; + GLfloat y2 = n0->y / density - 1 + by + y0*2; + + dist2 = (x - x2) * (x - x2) + (y - y2) * (y - y2); + if (dist2 < d2) + { + d2 = dist2; + nn = n0; + xoff = x0; + yoff = y0; + } + } + + bp->button_down_p = True; + bp->dragging = nn; + bp->drag_x = xoff; + bp->drag_y = yoff; + return True; + } + else if (event->xany.type == ButtonRelease) + { + bp->dragging = 0; + bp->button_down_p = False; + return True; + } + else if (event->xany.type == MotionNotify && bp->dragging) + { + GLfloat x = (GLfloat) event->xmotion.x / MI_WIDTH (mi) - 0.5; + GLfloat y = (GLfloat) event->xmotion.y / MI_HEIGHT (mi) - 0.5; + if (wire) x /= 0.2, y /= 0.2; + x -= bx; + y -= by; + x -= bp->drag_x * 2; + y -= bp->drag_y * 2; + bp->dragging->x = x * density + density; + bp->dragging->y = y * density + density; + bp->dragging->dx = bp->dragging->dy = 0; + return True; + } + else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event)) + { + dazzle_randomize (mi); + return True; + } + + return False; +} + + +ENTRYPOINT void +init_dazzle (ModeInfo *mi) +{ + dazzle_configuration *bp; + + MI_INIT (mi, bps); + + bp = &bps[MI_SCREEN(mi)]; + + bp->glx_context = init_GL(mi); + + if (!mode_arg || !*mode_arg || !strcasecmp(mode_arg, "random")) + bp->mode = RANDOM; + else if (!strcasecmp(mode_arg, "ship") || !strcasecmp(mode_arg, "ships")) + bp->mode = SHIPS; + else if (!strcasecmp(mode_arg, "flat")) + bp->mode = FLAT; + else + { + fprintf (stderr, "%s: mode must be ship, flat or random, not %s\n", + progname, mode_arg); + exit (1); + } + + bp->which_ship = -1; + if (bp->mode == SHIPS || bp->mode == RANDOM) + { + int i; + bp->dlists = (GLuint *) calloc (countof(all_ships)+1, sizeof(GLuint)); + for (i = 0; i < countof(all_ships); i++) + { + const struct gllist *gll = *all_ships[i]; + + bp->dlists[i] = glGenLists (1); + glNewList (bp->dlists[i], GL_COMPILE); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + + if (random() & 1) + { + glScalef (-1, 1, 1); + glTranslatef (-1, 0, 0); + } + renderList (gll, MI_IS_WIREFRAME(mi)); + + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glEndList (); + } + } + + dazzle_randomize (mi); + reshape_dazzle (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); +} + + +ENTRYPOINT void +draw_dazzle (ModeInfo *mi) +{ + dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; + Bool wire = MI_IS_WIREFRAME(mi); + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + int x, y; + + 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 (); + mi->polygon_count = 0; + + glTranslatef (0.5, 0.5, 0); + + if (wire) + glScalef (0.2, 0.2, 1); + + move_grid (mi); + + for (y = -1; y <= 1; y++) + for (x = -1; x <= 1; x++) + draw_grid (mi, x, y); + + if (bp->which_ship != -1) + { +# ifdef USE_IPHONE + int rot = current_device_rotation(); +# endif + + if (wire) + glColor3f (1, 0, 0); + else + { + glColor3f (0, 0, 0); + + /* Draw into the depth buffer but not the frame buffer */ + glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glClear (GL_DEPTH_BUFFER_BIT); + glEnable (GL_DEPTH_TEST); + } + +# ifdef USE_IPHONE + glRotatef (90, 0, 0, 1); + if (rot == 90 || rot == -270) + glRotatef (180, 0, 0, 1); +# endif + + glPushMatrix(); + glRotatef (90, 1, 0, 0); + glScalef (0.9, 0.9, 0.9); + glTranslatef (-0.5, 0, -0.2); + +# ifdef USE_IPHONE + if (rot == 0 || rot == 180 || rot == -180) + glScalef (1, 1, (GLfloat) MI_HEIGHT(mi) / MI_WIDTH(mi)); + else +# endif + glScalef (1, 1, (GLfloat) MI_WIDTH(mi) / MI_HEIGHT(mi)); + + /* Wave boat horizontally and vertically */ + glTranslatef (cos ((double) bp->frames / 80 * M_PI * speed) / 200, + 0, + cos ((double) bp->frames / 60 * M_PI * speed) / 300); + + glCallList (bp->dlists[bp->which_ship]); + mi->polygon_count += (*all_ships[bp->which_ship])->points / 3; + glPopMatrix(); + + /* Wave horizon vertically */ + glTranslatef (0, + cos ((double) bp->frames / 120 * M_PI * speed) / 200, + 0); + + if (! wire) + { + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + /* Black out everything that isn't a ship. */ +# if 0 + glBegin (GL_QUADS); + glVertex3f (-1, -1, 0); + glVertex3f (-1, 1, 0); + glVertex3f ( 1, 1, 0); + glVertex3f ( 1, -1, 0); + glEnd(); +# else + { + GLfloat horizon = 0.15; + + glColor3f (0.7, 0.7, 1.0); + glBegin (GL_QUADS); + glVertex3f (-1, -1, 0); + glVertex3f (-1, horizon, 0); + glVertex3f ( 1, horizon, 0); + glVertex3f ( 1, -1, 0); + glEnd(); + + glColor3f (0.0, 0.05, 0.2); + glBegin (GL_QUADS); + glVertex3f (-1, horizon, 0); + glVertex3f (-1, 1, 0); + glVertex3f ( 1, 1, 0); + glVertex3f ( 1, horizon, 0); + glEnd(); + } +# endif + + glDisable (GL_DEPTH_TEST); + } + } + + if (wire) + { + glColor3f(0,1,1); + glLineWidth(4); + glBegin(GL_LINE_LOOP); + 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(); + glLineWidth(1); + } + + glPopMatrix (); + + bp->frames++; + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + + +ENTRYPOINT void +free_dazzle (ModeInfo *mi) +{ + dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; + if (bp->nodes) free (bp->nodes); + if (bp->colors) free (bp->colors); + if (bp->dlists) + { + int i; + for (i = 0; i < countof(all_ships); i++) + glDeleteLists (bp->dlists[i], 1); + free (bp->dlists); + } +} + +XSCREENSAVER_MODULE_2 ("RazzleDazzle", razzledazzle, dazzle) + +#endif /* USE_GL */