From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 16 Oct 2018 10:08:48 +0200 Subject: Original 5.40 --- hacks/glx/glmatrix.c | 1068 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1068 insertions(+) create mode 100644 hacks/glx/glmatrix.c (limited to 'hacks/glx/glmatrix.c') diff --git a/hacks/glx/glmatrix.c b/hacks/glx/glmatrix.c new file mode 100644 index 0000000..6a9b1fe --- /dev/null +++ b/hacks/glx/glmatrix.c @@ -0,0 +1,1068 @@ +/* glmatrix, Copyright (c) 2003-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. + * + * GLMatrix -- simulate the text scrolls from the movie "The Matrix". + * + * This program does a 3D rendering of the dropping characters that + * appeared in the title sequences of the movies. See also `xmatrix' + * for a simulation of what the computer monitors actually *in* the + * movie did. + */ + +#define DEFAULTS "*delay: 30000 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + +# define free_matrix 0 +# define release_matrix 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#undef BELLRAND +#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) + +#include "xlockmore.h" +#include "ximage-loader.h" + +#include "images/gen/matrix3_png.h" + +#ifdef USE_GL /* whole file */ + + +#define DEF_SPEED "1.0" +#define DEF_DENSITY "20" +#define DEF_CLOCK "False" +#define DEF_FOG "True" +#define DEF_WAVES "True" +#define DEF_ROTATE "True" +#define DEF_TEXTURE "True" +#define DEF_MODE "Matrix" +#define DEF_TIMEFMT " %l%M%p " + + +#define CHAR_COLS 16 +#define CHAR_ROWS 13 + +static const int matrix_encoding[] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, +# if 0 + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207 +# else + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175 +# endif + }; +static const int decimal_encoding[] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; +static const int hex_encoding[] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 }; +static const int binary_encoding[] = { 16, 17 }; +static const int dna_encoding[] = { 33, 35, 39, 52 }; + +static const unsigned char char_map[256] = { + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */ + 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */ + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */ + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */ + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */ + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */ + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */ + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */ +#if 0 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */ + 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */ +#else /* see spank_image() */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */ +#endif +}; + +#define CURSOR_GLYPH 97 + +/* #define DEBUG */ + +#define GRID_SIZE 70 /* width and height of the arena */ +#define GRID_DEPTH 35 /* depth of the arena */ +#define WAVE_SIZE 22 /* periodicity of color (brightness) waves */ +#define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */ + +static const struct { GLfloat x, y; } nice_views[] = { + { 0, 0 }, + { 0, -20 }, /* this is a list of viewer rotations that look nice. */ + { 0, 20 }, /* every now and then we switch to a new one. */ + { 25, 0 }, /* (but we only use the first one at start-up.) */ + {-25, 0 }, + { 25, 20 }, + {-25, 20 }, + { 25, -20 }, + {-25, -20 }, + + { 10, 0 }, + {-10, 0 }, + { 0, 0 }, /* prefer these */ + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, +}; + + +typedef struct { + GLfloat x, y, z; /* position of strip */ + GLfloat dx, dy, dz; /* velocity of strip */ + + Bool erasing_p; /* Whether this strip is on its way out. */ + + int spinner_glyph; /* the bottommost glyph -- the feeder */ + GLfloat spinner_y; /* where on the strip the bottom glyph is */ + GLfloat spinner_speed; /* how fast the bottom glyph drops */ + + int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be + revealed by the dropping spinner. + 0 means no glyph; negative means "spinner". + If non-zero, real value is abs(G)-1. */ + + Bool highlight[GRID_SIZE]; + /* some glyphs may be highlighted */ + + int spin_speed; /* Rotate all spinners every this-many frames */ + int spin_tick; /* frame counter */ + + int wave_position; /* Waves of brightness wash down the strip. */ + int wave_speed; /* every this-many frames. */ + int wave_tick; /* frame counter. */ + +} strip; + + +typedef struct { + GLXContext *glx_context; + Bool button_down_p; + GLuint texture; + int nstrips; + strip *strips; + const int *glyph_map; + int nglyphs; + GLfloat tex_char_width, tex_char_height; + + /* auto-tracking direction of view */ + int last_view, target_view; + GLfloat view_x, view_y; + int view_steps, view_tick; + Bool auto_tracking_p; + int track_tick; + + int real_char_rows; + GLfloat brightness_ramp[WAVE_SIZE]; + +} matrix_configuration; + +static matrix_configuration *mps = NULL; + +static GLfloat speed; +static GLfloat density; +static Bool do_clock; +static char *timefmt; +static Bool do_fog; +static Bool do_waves; +static Bool do_rotate; +static Bool do_texture; +static char *mode_str; + +static XrmOptionDescRec opts[] = { + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-density", ".density", XrmoptionSepArg, 0 }, + { "-mode", ".mode", XrmoptionSepArg, 0 }, + { "-binary", ".mode", XrmoptionNoArg, "binary" }, + { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" }, + { "-decimal", ".mode", XrmoptionNoArg, "decimal" }, + { "-dna", ".mode", XrmoptionNoArg, "dna" }, + { "-clock", ".clock", XrmoptionNoArg, "True" }, + { "+clock", ".clock", XrmoptionNoArg, "False" }, + { "-timefmt", ".timefmt", XrmoptionSepArg, 0 }, + { "-fog", ".fog", XrmoptionNoArg, "True" }, + { "+fog", ".fog", XrmoptionNoArg, "False" }, + { "-waves", ".waves", XrmoptionNoArg, "True" }, + { "+waves", ".waves", XrmoptionNoArg, "False" }, + { "-rotate", ".rotate", XrmoptionNoArg, "True" }, + { "+rotate", ".rotate", XrmoptionNoArg, "False" }, + {"-texture", ".texture", XrmoptionNoArg, "True" }, + {"+texture", ".texture", XrmoptionNoArg, "False" }, +}; + +static argtype vars[] = { + {&mode_str, "mode", "Mode", DEF_MODE, t_String}, + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&density, "density", "Density", DEF_DENSITY, t_Float}, + {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool}, + {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String}, + {&do_fog, "fog", "Fog", DEF_FOG, t_Bool}, + {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool}, + {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool}, + {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool}, +}; + +ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL}; + + +/* Re-randomize the state of one strip. + */ +static void +reset_strip (ModeInfo *mi, strip *s) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + int i; + Bool time_displayed_p = False; /* never display time twice in one strip */ + + memset (s, 0, sizeof(*s)); + s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2)); + s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */ + s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7); + s->spinner_y = 0; + + s->dx = 0; +/* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */ + s->dy = 0; + s->dz = (BELLRAND(0.02) * speed); + + s->spinner_speed = (BELLRAND(0.3) * speed); + + s->spin_speed = (int) BELLRAND(2.0 / speed) + 1; + s->spin_tick = 0; + + s->wave_position = 0; + s->wave_speed = (int) BELLRAND(3.0 / speed) + 1; + s->wave_tick = 0; + + for (i = 0; i < GRID_SIZE; i++) + if (do_clock && + !time_displayed_p && + (i < GRID_SIZE-5) && /* display approx. once per 5 strips */ + !(random() % (GRID_SIZE-5)*5)) + { + int j; + char text[80]; + time_t now = time ((time_t *) 0); + struct tm *tm = localtime (&now); + strftime (text, sizeof(text)-1, timefmt, tm); + + /* render time into the strip */ + for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++) + { + s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1; + s->highlight[i] = True; + } + + time_displayed_p = True; + } + else + { + int draw_p = (random() % 7); + int spin_p = (draw_p && !(random() % 20)); + int g = (draw_p + ? mp->glyph_map[(random() % mp->nglyphs)] + 1 + : 0); + if (spin_p) g = -g; + s->glyphs[i] = g; + s->highlight[i] = False; + } + + s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1); +} + + +/* Animate the strip one step. Reset if it has reached the bottom. + */ +static void +tick_strip (ModeInfo *mi, strip *s) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + int i; + + if (mp->button_down_p) + return; + + s->x += s->dx; + s->y += s->dy; + s->z += s->dz; + + if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */ + { + reset_strip (mi, s); + return; + } + + s->spinner_y += s->spinner_speed; + if (s->spinner_y >= GRID_SIZE) + { + if (s->erasing_p) + { + reset_strip (mi, s); + return; + } + else + { + s->erasing_p = True; + s->spinner_y = 0; + s->spinner_speed /= 2; /* erase it slower than we drew it */ + } + } + + /* Spin the spinners. */ + s->spin_tick++; + if (s->spin_tick > s->spin_speed) + { + s->spin_tick = 0; + s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1); + for (i = 0; i < GRID_SIZE; i++) + if (s->glyphs[i] < 0) + { + s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1); + if (! (random() % 800)) /* sometimes they stop spinning */ + s->glyphs[i] = -s->glyphs[i]; + } + } + + /* Move the color (brightness) wave. */ + s->wave_tick++; + if (s->wave_tick > s->wave_speed) + { + s->wave_tick = 0; + s->wave_position++; + if (s->wave_position >= WAVE_SIZE) + s->wave_position = 0; + } +} + + +/* Draw a single character at the given position and brightness. + */ +static void +draw_glyph (ModeInfo *mi, int glyph, Bool highlight, + GLfloat x, GLfloat y, GLfloat z, + GLfloat brightness) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + GLfloat w = mp->tex_char_width; + GLfloat h = mp->tex_char_height; + GLfloat cx = 0, cy = 0; + GLfloat S = 1; + Bool spinner_p = (glyph < 0); + + if (glyph == 0) abort(); + if (glyph < 0) glyph = -glyph; + + if (spinner_p) + brightness *= 1.5; + + if (!do_texture) + { + S = 0.8; + x += 0.1; + y += 0.1; + } + else + { + int ccx = ((glyph - 1) % CHAR_COLS); + int ccy = ((glyph - 1) / CHAR_COLS); + + cx = ccx * w; + cy = (mp->real_char_rows - ccy - 1) * h; + + if (do_fog) + { + GLfloat depth; + depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */ + depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */ + brightness *= depth; /* so no row goes all black. */ + } + } + + { + GLfloat r, g, b, a; + + if (highlight) + brightness *= 2; + + if (!do_texture && !spinner_p) + r = b = 0, g = 1; + else + r = g = b = 1; + + a = brightness; + + /* If the glyph is very close to the screen (meaning it is very large, + and is about to splash into the screen and vanish) then start fading + it out, proportional to how close to the glass it is. + */ + if (z > GRID_DEPTH/2) + { + GLfloat ratio = ((z - GRID_DEPTH/2) / + ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2)); + int i = ratio * WAVE_SIZE; + + if (i < 0) i = 0; + else if (i >= WAVE_SIZE) i = WAVE_SIZE-1; + + a *= mp->brightness_ramp[i]; + } + + glColor4f (r,g,b,a); + } + + glBegin (wire ? GL_LINE_LOOP : GL_QUADS); + glNormal3f (0, 0, 1); + glTexCoord2f (cx, cy); glVertex3f (x, y, z); + glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z); + glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z); + glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z); + glEnd (); + + if (wire && spinner_p) + { + glBegin (GL_LINES); + glVertex3f (x, y, z); + glVertex3f (x+S, y+S, z); + glVertex3f (x, y+S, z); + glVertex3f (x+S, y, z); + glEnd(); + } + + mi->polygon_count++; +} + + +/* Draw all the visible glyphs in the strip. + */ +static void +draw_strip (ModeInfo *mi, strip *s) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + int i; + for (i = 0; i < GRID_SIZE; i++) + { + int g = s->glyphs[i]; + Bool below_p = (s->spinner_y >= i); + + if (s->erasing_p) + below_p = !below_p; + + if (g && below_p) /* don't draw cells below the spinner */ + { + GLfloat brightness; + if (!do_waves) + brightness = 1.0; + else + { + int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position)) + % WAVE_SIZE); + brightness = mp->brightness_ramp[j]; + } + + draw_glyph (mi, g, s->highlight[i], + s->x, s->y - i, s->z, brightness); + } + } + + if (!s->erasing_p) + draw_glyph (mi, s->spinner_glyph, False, + s->x, s->y - s->spinner_y, s->z, 1.0); +} + + +/* qsort comparator for sorting strips by z position */ +static int +cmp_strips (const void *aa, const void *bb) +{ + const strip *a = *(strip **) aa; + const strip *b = *(strip **) bb; + return ((int) (a->z * 10000) - + (int) (b->z * 10000)); +} + + +/* Auto-tracking + */ + +static void +auto_track_init (ModeInfo *mi) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + mp->last_view = 0; + mp->target_view = 0; + mp->view_x = nice_views[mp->last_view].x; + mp->view_y = nice_views[mp->last_view].y; + mp->view_steps = 100; + mp->view_tick = 0; + mp->auto_tracking_p = False; +} + + +static void +auto_track (ModeInfo *mi) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + + if (! do_rotate) + return; + if (mp->button_down_p) + return; + + /* if we're not moving, maybe start moving. Otherwise, do nothing. */ + if (! mp->auto_tracking_p) + { + if (++mp->track_tick < 20/speed) return; + mp->track_tick = 0; + if (! (random() % 20)) + mp->auto_tracking_p = True; + else + return; + } + + + { + GLfloat ox = nice_views[mp->last_view].x; + GLfloat oy = nice_views[mp->last_view].y; + GLfloat tx = nice_views[mp->target_view].x; + GLfloat ty = nice_views[mp->target_view].y; + + /* move from A to B with sinusoidal deltas, so that it doesn't jerk + to a stop. */ + GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps); + + mp->view_x = (ox + ((tx - ox) * th)); + mp->view_y = (oy + ((ty - oy) * th)); + mp->view_tick++; + + if (mp->view_tick >= mp->view_steps) + { + mp->view_tick = 0; + mp->view_steps = (350.0 / speed); + mp->last_view = mp->target_view; + mp->target_view = (random() % (countof(nice_views) - 1)) + 1; + mp->auto_tracking_p = False; + } + } +} + + +/* Window management, etc + */ +ENTRYPOINT void +reshape_matrix (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 (80.0, 1/h, 1.0, 100); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( 0.0, 0.0, 25.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); + + glClear(GL_COLOR_BUFFER_BIT); +} + + +ENTRYPOINT Bool +matrix_handle_event (ModeInfo *mi, XEvent *event) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + + if (event->xany.type == ButtonPress && + event->xbutton.button == Button1) + { + mp->button_down_p = True; + return True; + } + else if (event->xany.type == ButtonRelease && + event->xbutton.button == Button1) + { + mp->button_down_p = False; + return True; + } + + return False; +} + + +#if 0 +static Bool +bigendian (void) +{ + union { int i; char c[sizeof(int)]; } u; + u.i = 1; + return !u.c[0]; +} +#endif + + +/* The image with the characters in it is 512x598, meaning that it needs to + be copied into a 512x1024 texture. But some machines can't handle textures + that large... And it turns out that we aren't using most of the characters + in that image anyway, since this program doesn't do anything that makes use + of the full range of Latin1 characters. So... this function tosses out the + last 32 of the Latin1 characters, resulting in a 512x506 image, which we + can then stuff in a 512x512 texture. Voila. + + If this hack ever grows into something that displays full Latin1 text, + well then, Something Else Will Need To Be Done. + + Since currently GLMatrix does not run textclient / xscreensaver-text, + it's not an issue. (XMatrix does that.) + + */ +static void +spank_image (matrix_configuration *mp, XImage *xi) +{ + int ch = xi->height / CHAR_ROWS; + int cut = 2; + unsigned char *bits = (unsigned char *) xi->data; + unsigned char *from, *to, *s, *end; + int L = xi->bytes_per_line * ch; +/* int i;*/ + + /* Copy row 12 into 10 (which really means, copy 2 into 0, + since texture data is upside down.). + */ + to = bits + (L * cut); + from = bits; + end = from + L; + s = from; + while (s < end) + *to++ = *s++; + + /* Then, pull all the bits down by 2 rows. + */ + to = bits; + from = bits + (L * cut); + end = bits + (L * CHAR_ROWS); + s = from; + while (s < end) + *to++ = *s++; + + /* And clear out the rest, for good measure. + */ + from = bits + (L * (CHAR_ROWS - cut)); + end = bits + (L * CHAR_ROWS); + s = from; + while (s < end) + *s++ = 0; + + xi->height -= (cut * ch); + mp->real_char_rows -= cut; + +# if 0 + /* Finally, pull the map indexes back to match the new bits. + */ + for (i = 0; i < countof(matrix_encoding); i++) + if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut))) + matrix_encoding[i] -= (cut * CHAR_COLS); +# endif +} + + +static void +load_textures (ModeInfo *mi, Bool flip_p) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + XImage *xi; + int x, y; + int cw, ch; + int orig_w, orig_h; + + /* The Matrix image is 512x598 -- but GL texture sizes must be powers of 2. + So we waste some padding rows to round up. + */ + xi = image_data_to_ximage (mi->dpy, mi->xgwa.visual, + matrix3_png, sizeof(matrix3_png)); + orig_w = xi->width; + orig_h = xi->height; + mp->real_char_rows = CHAR_ROWS; + spank_image (mp, xi); + + if (xi->height != 512 && xi->height != 1024) + { + xi->height = (xi->height < 512 ? 512 : 1024); + xi->data = realloc (xi->data, xi->height * xi->bytes_per_line); + if (!xi->data) + { + fprintf(stderr, "%s: out of memory\n", progname); + exit(1); + } + } + + if (xi->width != 512) abort(); + if (xi->height != 512 && xi->height != 1024) abort(); + + /* char size in pixels */ + cw = orig_w / CHAR_COLS; + ch = orig_h / CHAR_ROWS; + + /* char size in ratio of final (padded) texture size */ + mp->tex_char_width = (GLfloat) cw / xi->width; + mp->tex_char_height = (GLfloat) ch / xi->height; + + /* Flip each character's bits horizontally -- we could also just do this + by reversing the texture coordinates on the quads, but on some systems + that slows things down a lot. + */ + if (flip_p) + { + int xx, col; + unsigned long buf[100]; + for (y = 0; y < xi->height; y++) + for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw) + { + for (x = 0; x < cw; x++) + buf[x] = XGetPixel (xi, xx+x, y); + for (x = 0; x < cw; x++) + XPutPixel (xi, xx+x, y, buf[cw-x-1]); + } + } + + /* The pixmap is a color image with no transparency. Set the texture's + alpha to be the green channel, and set the green channel to be 100%. + */ + { + int rpos, gpos, bpos, apos; /* bitfield positions */ +#if 0 + /* #### Cherub says that the little-endian case must be taken on MacOSX, + or else the colors/alpha are the wrong way around. How can + that be the case? + */ + if (bigendian()) + rpos = 24, gpos = 16, bpos = 8, apos = 0; + else +#endif + rpos = 0, gpos = 8, bpos = 16, apos = 24; + + for (y = 0; y < xi->height; y++) + for (x = 0; x < xi->width; x++) + { + unsigned long p = XGetPixel (xi, x, y); + unsigned char r = (p >> rpos) & 0xFF; + unsigned char g = (p >> gpos) & 0xFF; + unsigned char b = (p >> bpos) & 0xFF; + unsigned char a = g; + g = 0xFF; + p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos); + XPutPixel (xi, x, y, p); + } + } + + /* Now load the texture into GL. + */ + clear_gl_error(); + glGenTextures (1, &mp->texture); + + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + /* messes up -fps */ + /* glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);*/ + glBindTexture (GL_TEXTURE_2D, mp->texture); + check_gl_error ("texture init"); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, xi->data); + { + char buf[255]; + sprintf (buf, "creating %dx%d texture:", xi->width, xi->height); + check_gl_error (buf); + } + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + /* I'd expect CLAMP to be the thing to do here, but oddly, we get a + faint solid green border around the texture if it is *not* REPEAT! + */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + check_gl_error ("texture param"); + + XDestroyImage (xi); +} + + +ENTRYPOINT void +init_matrix (ModeInfo *mi) +{ + matrix_configuration *mp; + int wire = MI_IS_WIREFRAME(mi); + Bool flip_p = 0; + int i; + + if (wire) + do_texture = False; + + MI_INIT (mi, mps); + + mp = &mps[MI_SCREEN(mi)]; + mp->glx_context = init_GL(mi); + + if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix")) + { + flip_p = 1; + mp->glyph_map = matrix_encoding; + mp->nglyphs = countof(matrix_encoding); + } + else if (!strcasecmp (mode_str, "dna")) + { + flip_p = 0; + mp->glyph_map = dna_encoding; + mp->nglyphs = countof(dna_encoding); + } + else if (!strcasecmp (mode_str, "bin") || + !strcasecmp (mode_str, "binary")) + { + flip_p = 0; + mp->glyph_map = binary_encoding; + mp->nglyphs = countof(binary_encoding); + } + else if (!strcasecmp (mode_str, "hex") || + !strcasecmp (mode_str, "hexadecimal")) + { + flip_p = 0; + mp->glyph_map = hex_encoding; + mp->nglyphs = countof(hex_encoding); + } + else if (!strcasecmp (mode_str, "dec") || + !strcasecmp (mode_str, "decimal")) + { + flip_p = 0; + mp->glyph_map = decimal_encoding; + mp->nglyphs = countof(decimal_encoding); + } + else + { + fprintf (stderr, + "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n", + progname, mode_str); + exit (1); + } + + reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + + glShadeModel(GL_SMOOTH); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_NORMALIZE); + + if (do_texture) + load_textures (mi, flip_p); + + /* to scale coverage-percent to strips, this number looks about right... */ + mp->nstrips = (int) (density * 2.2); + if (mp->nstrips < 1) mp->nstrips = 1; + else if (mp->nstrips > 2000) mp->nstrips = 2000; + + + mp->strips = calloc (mp->nstrips, sizeof(strip)); + for (i = 0; i < mp->nstrips; i++) + { + strip *s = &mp->strips[i]; + reset_strip (mi, s); + + /* If we start all strips from zero at once, then the first few seconds + of the animation are much denser than normal. So instead, set all + the initial strips to erase-mode with random starting positions. + As these die off at random speeds and are re-created, we'll get a + more consistent density. */ + s->erasing_p = True; + s->spinner_y = frand(GRID_SIZE); + memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */ + } + + /* Compute the brightness ramp. + */ + for (i = 0; i < WAVE_SIZE; i++) + { + GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1)); + j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */ + j = sin (j); /* j ranges from 0.0 - 1.0 */ + j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */ + mp->brightness_ramp[i] = j; + /* printf("%2d %8.2f\n", i, j); */ + } + + + auto_track_init (mi); +} + + +#ifdef DEBUG + +static void +draw_grid (ModeInfo *mi) +{ + if (!MI_IS_WIREFRAME(mi)) + { + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + glPushMatrix(); + + glColor3f(1, 1, 1); + glBegin(GL_LINES); + glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0); + glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0); + glEnd(); + glBegin(GL_LINE_LOOP); + glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0); + glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0); + glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0); + glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0); + glEnd(); + glBegin(GL_LINE_LOOP); + glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2); + glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2); + glEnd(); + glBegin(GL_LINE_LOOP); + glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2); + glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2); + glEnd(); + glBegin(GL_LINES); + glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2); + glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2); + glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2); + glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2); + glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2); + glEnd(); + glPopMatrix(); + if (!MI_IS_WIREFRAME(mi)) + { + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + } +} +#endif /* DEBUG */ + + +ENTRYPOINT void +draw_matrix (ModeInfo *mi) +{ + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + int i; + + if (!mp->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context)); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix (); + glRotatef(current_device_rotation(), 0, 0, 1); + + if (do_texture) + { + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + /* Jeff Epler points out: + By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are + added to each other, so that a bright glyph with a darker one + in front is a little brighter than the bright glyph alone. + */ + glBlendFunc (GL_SRC_ALPHA, GL_ONE); + } + + if (do_rotate) + { + glRotatef (mp->view_x, 1, 0, 0); + glRotatef (mp->view_y, 0, 1, 0); + } + +#ifdef DEBUG +# if 0 + glScalef(0.5, 0.5, 0.5); +# endif +# if 0 + glRotatef(-30, 0, 1, 0); +# endif + draw_grid (mi); +#endif + + mi->polygon_count = 0; + + /* Render (and tick) each strip, starting at the back + (draw the ones farthest from the camera first, to make + the alpha transparency work out right.) + */ + { + strip **sorted = malloc (mp->nstrips * sizeof(*sorted)); + for (i = 0; i < mp->nstrips; i++) + sorted[i] = &mp->strips[i]; + qsort (sorted, i, sizeof(*sorted), cmp_strips); + + for (i = 0; i < mp->nstrips; i++) + { + strip *s = sorted[i]; + tick_strip (mi, s); + draw_strip (mi, s); + } + free (sorted); + } + + auto_track (mi); + +#if 0 + glBegin(GL_QUADS); + glColor3f(1,1,1); + glTexCoord2f (0,0); glVertex3f(-15,-15,0); + glTexCoord2f (0,1); glVertex3f(-15,15,0); + glTexCoord2f (1,1); glVertex3f(15,15,0); + glTexCoord2f (1,0); glVertex3f(15,-15,0); + glEnd(); +#endif + + glPopMatrix (); + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + +XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix) + +#endif /* USE_GL */ -- cgit v1.2.3-55-g7522