1 /* glmatrix, Copyright (c) 2003-2018 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * GLMatrix -- simulate the text scrolls from the movie "The Matrix".
13 * This program does a 3D rendering of the dropping characters that
14 * appeared in the title sequences of the movies. See also `xmatrix'
15 * for a simulation of what the computer monitors actually *in* the
19 #define DEFAULTS "*delay: 30000 \n" \
20 "*showFPS: False \n" \
21 "*wireframe: False \n" \
23 # define free_matrix 0
24 # define release_matrix 0
26 #define countof(x) (sizeof((x))/sizeof((*x)))
29 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
31 #include "xlockmore.h"
32 #include "ximage-loader.h"
34 #include "images/gen/matrix3_png.h"
36 #ifdef USE_GL /* whole file */
39 #define DEF_SPEED "1.0"
40 #define DEF_DENSITY "20"
41 #define DEF_CLOCK "False"
42 #define DEF_FOG "True"
43 #define DEF_WAVES "True"
44 #define DEF_ROTATE "True"
45 #define DEF_TEXTURE "True"
46 #define DEF_MODE "Matrix"
47 #define DEF_TIMEFMT " %l%M%p "
53 static const int matrix_encoding[] = {
54 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
56 192, 193, 194, 195, 196, 197, 198, 199,
57 200, 201, 202, 203, 204, 205, 206, 207
59 160, 161, 162, 163, 164, 165, 166, 167,
60 168, 169, 170, 171, 172, 173, 174, 175
63 static const int decimal_encoding[] = {
64 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
65 static const int hex_encoding[] = {
66 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 };
67 static const int binary_encoding[] = { 16, 17 };
68 static const int dna_encoding[] = { 33, 35, 39, 52 };
70 static const unsigned char char_map[256] = {
71 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */
72 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */
73 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
74 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
75 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
76 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
77 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
78 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
79 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */
80 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */
81 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
82 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
83 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
84 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
86 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
87 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
88 #else /* see spank_image() */
89 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */
90 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */
94 #define CURSOR_GLYPH 97
98 #define GRID_SIZE 70 /* width and height of the arena */
99 #define GRID_DEPTH 35 /* depth of the arena */
100 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
101 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
103 static const struct { GLfloat x, y; } nice_views[] = {
105 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
106 { 0, 20 }, /* every now and then we switch to a new one. */
107 { 25, 0 }, /* (but we only use the first one at start-up.) */
116 { 0, 0 }, /* prefer these */
125 GLfloat x, y, z; /* position of strip */
126 GLfloat dx, dy, dz; /* velocity of strip */
128 Bool erasing_p; /* Whether this strip is on its way out. */
130 int spinner_glyph; /* the bottommost glyph -- the feeder */
131 GLfloat spinner_y; /* where on the strip the bottom glyph is */
132 GLfloat spinner_speed; /* how fast the bottom glyph drops */
134 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
135 revealed by the dropping spinner.
136 0 means no glyph; negative means "spinner".
137 If non-zero, real value is abs(G)-1. */
139 Bool highlight[GRID_SIZE];
140 /* some glyphs may be highlighted */
142 int spin_speed; /* Rotate all spinners every this-many frames */
143 int spin_tick; /* frame counter */
145 int wave_position; /* Waves of brightness wash down the strip. */
146 int wave_speed; /* every this-many frames. */
147 int wave_tick; /* frame counter. */
153 GLXContext *glx_context;
158 const int *glyph_map;
160 GLfloat tex_char_width, tex_char_height;
162 /* auto-tracking direction of view */
163 int last_view, target_view;
164 GLfloat view_x, view_y;
165 int view_steps, view_tick;
166 Bool auto_tracking_p;
170 GLfloat brightness_ramp[WAVE_SIZE];
172 } matrix_configuration;
174 static matrix_configuration *mps = NULL;
176 static GLfloat speed;
177 static GLfloat density;
178 static Bool do_clock;
179 static char *timefmt;
181 static Bool do_waves;
182 static Bool do_rotate;
183 static Bool do_texture;
184 static char *mode_str;
186 static XrmOptionDescRec opts[] = {
187 { "-speed", ".speed", XrmoptionSepArg, 0 },
188 { "-density", ".density", XrmoptionSepArg, 0 },
189 { "-mode", ".mode", XrmoptionSepArg, 0 },
190 { "-binary", ".mode", XrmoptionNoArg, "binary" },
191 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
192 { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
193 { "-dna", ".mode", XrmoptionNoArg, "dna" },
194 { "-clock", ".clock", XrmoptionNoArg, "True" },
195 { "+clock", ".clock", XrmoptionNoArg, "False" },
196 { "-timefmt", ".timefmt", XrmoptionSepArg, 0 },
197 { "-fog", ".fog", XrmoptionNoArg, "True" },
198 { "+fog", ".fog", XrmoptionNoArg, "False" },
199 { "-waves", ".waves", XrmoptionNoArg, "True" },
200 { "+waves", ".waves", XrmoptionNoArg, "False" },
201 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
202 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
203 {"-texture", ".texture", XrmoptionNoArg, "True" },
204 {"+texture", ".texture", XrmoptionNoArg, "False" },
207 static argtype vars[] = {
208 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
209 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
210 {&density, "density", "Density", DEF_DENSITY, t_Float},
211 {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool},
212 {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String},
213 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
214 {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
215 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
216 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
219 ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
222 /* Re-randomize the state of one strip.
225 reset_strip (ModeInfo *mi, strip *s)
227 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
229 Bool time_displayed_p = False; /* never display time twice in one strip */
231 memset (s, 0, sizeof(*s));
232 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
233 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
234 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
238 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
240 s->dz = (BELLRAND(0.02) * speed);
242 s->spinner_speed = (BELLRAND(0.3) * speed);
244 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
247 s->wave_position = 0;
248 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
251 for (i = 0; i < GRID_SIZE; i++)
254 (i < GRID_SIZE-5) && /* display approx. once per 5 strips */
255 !(random() % (GRID_SIZE-5)*5))
259 time_t now = time ((time_t *) 0);
260 struct tm *tm = localtime (&now);
261 strftime (text, sizeof(text)-1, timefmt, tm);
263 /* render time into the strip */
264 for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
266 s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
267 s->highlight[i] = True;
270 time_displayed_p = True;
274 int draw_p = (random() % 7);
275 int spin_p = (draw_p && !(random() % 20));
277 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
281 s->highlight[i] = False;
284 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
288 /* Animate the strip one step. Reset if it has reached the bottom.
291 tick_strip (ModeInfo *mi, strip *s)
293 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
296 if (mp->button_down_p)
303 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
309 s->spinner_y += s->spinner_speed;
310 if (s->spinner_y >= GRID_SIZE)
321 s->spinner_speed /= 2; /* erase it slower than we drew it */
325 /* Spin the spinners. */
327 if (s->spin_tick > s->spin_speed)
330 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
331 for (i = 0; i < GRID_SIZE; i++)
332 if (s->glyphs[i] < 0)
334 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
335 if (! (random() % 800)) /* sometimes they stop spinning */
336 s->glyphs[i] = -s->glyphs[i];
340 /* Move the color (brightness) wave. */
342 if (s->wave_tick > s->wave_speed)
346 if (s->wave_position >= WAVE_SIZE)
347 s->wave_position = 0;
352 /* Draw a single character at the given position and brightness.
355 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
356 GLfloat x, GLfloat y, GLfloat z,
359 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
360 int wire = MI_IS_WIREFRAME(mi);
361 GLfloat w = mp->tex_char_width;
362 GLfloat h = mp->tex_char_height;
363 GLfloat cx = 0, cy = 0;
365 Bool spinner_p = (glyph < 0);
367 if (glyph == 0) abort();
368 if (glyph < 0) glyph = -glyph;
381 int ccx = ((glyph - 1) % CHAR_COLS);
382 int ccy = ((glyph - 1) / CHAR_COLS);
385 cy = (mp->real_char_rows - ccy - 1) * h;
390 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
391 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
392 brightness *= depth; /* so no row goes all black. */
402 if (!do_texture && !spinner_p)
409 /* If the glyph is very close to the screen (meaning it is very large,
410 and is about to splash into the screen and vanish) then start fading
411 it out, proportional to how close to the glass it is.
413 if (z > GRID_DEPTH/2)
415 GLfloat ratio = ((z - GRID_DEPTH/2) /
416 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
417 int i = ratio * WAVE_SIZE;
420 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
422 a *= mp->brightness_ramp[i];
428 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
429 glNormal3f (0, 0, 1);
430 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
431 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
432 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
433 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
436 if (wire && spinner_p)
439 glVertex3f (x, y, z);
440 glVertex3f (x+S, y+S, z);
441 glVertex3f (x, y+S, z);
442 glVertex3f (x+S, y, z);
450 /* Draw all the visible glyphs in the strip.
453 draw_strip (ModeInfo *mi, strip *s)
455 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
457 for (i = 0; i < GRID_SIZE; i++)
459 int g = s->glyphs[i];
460 Bool below_p = (s->spinner_y >= i);
465 if (g && below_p) /* don't draw cells below the spinner */
472 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
474 brightness = mp->brightness_ramp[j];
477 draw_glyph (mi, g, s->highlight[i],
478 s->x, s->y - i, s->z, brightness);
483 draw_glyph (mi, s->spinner_glyph, False,
484 s->x, s->y - s->spinner_y, s->z, 1.0);
488 /* qsort comparator for sorting strips by z position */
490 cmp_strips (const void *aa, const void *bb)
492 const strip *a = *(strip **) aa;
493 const strip *b = *(strip **) bb;
494 return ((int) (a->z * 10000) -
495 (int) (b->z * 10000));
503 auto_track_init (ModeInfo *mi)
505 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
508 mp->view_x = nice_views[mp->last_view].x;
509 mp->view_y = nice_views[mp->last_view].y;
510 mp->view_steps = 100;
512 mp->auto_tracking_p = False;
517 auto_track (ModeInfo *mi)
519 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
523 if (mp->button_down_p)
526 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
527 if (! mp->auto_tracking_p)
529 if (++mp->track_tick < 20/speed) return;
531 if (! (random() % 20))
532 mp->auto_tracking_p = True;
539 GLfloat ox = nice_views[mp->last_view].x;
540 GLfloat oy = nice_views[mp->last_view].y;
541 GLfloat tx = nice_views[mp->target_view].x;
542 GLfloat ty = nice_views[mp->target_view].y;
544 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
546 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
548 mp->view_x = (ox + ((tx - ox) * th));
549 mp->view_y = (oy + ((ty - oy) * th));
552 if (mp->view_tick >= mp->view_steps)
555 mp->view_steps = (350.0 / speed);
556 mp->last_view = mp->target_view;
557 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
558 mp->auto_tracking_p = False;
564 /* Window management, etc
567 reshape_matrix (ModeInfo *mi, int width, int height)
569 GLfloat h = (GLfloat) height / (GLfloat) width;
572 if (width > height * 5) { /* tiny window: show middle */
573 height = width * 9/16;
575 h = height / (GLfloat) width;
578 glViewport (0, y, (GLint) width, (GLint) height);
580 glMatrixMode(GL_PROJECTION);
582 gluPerspective (80.0, 1/h, 1.0, 100);
584 glMatrixMode(GL_MODELVIEW);
586 gluLookAt( 0.0, 0.0, 25.0,
590 glClear(GL_COLOR_BUFFER_BIT);
595 matrix_handle_event (ModeInfo *mi, XEvent *event)
597 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
599 if (event->xany.type == ButtonPress &&
600 event->xbutton.button == Button1)
602 mp->button_down_p = True;
605 else if (event->xany.type == ButtonRelease &&
606 event->xbutton.button == Button1)
608 mp->button_down_p = False;
620 union { int i; char c[sizeof(int)]; } u;
627 /* The image with the characters in it is 512x598, meaning that it needs to
628 be copied into a 512x1024 texture. But some machines can't handle textures
629 that large... And it turns out that we aren't using most of the characters
630 in that image anyway, since this program doesn't do anything that makes use
631 of the full range of Latin1 characters. So... this function tosses out the
632 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
633 can then stuff in a 512x512 texture. Voila.
635 If this hack ever grows into something that displays full Latin1 text,
636 well then, Something Else Will Need To Be Done.
638 Since currently GLMatrix does not run textclient / xscreensaver-text,
639 it's not an issue. (XMatrix does that.)
643 spank_image (matrix_configuration *mp, XImage *xi)
645 int ch = xi->height / CHAR_ROWS;
647 unsigned char *bits = (unsigned char *) xi->data;
648 unsigned char *from, *to, *s, *end;
649 int L = xi->bytes_per_line * ch;
652 /* Copy row 12 into 10 (which really means, copy 2 into 0,
653 since texture data is upside down.).
655 to = bits + (L * cut);
662 /* Then, pull all the bits down by 2 rows.
665 from = bits + (L * cut);
666 end = bits + (L * CHAR_ROWS);
671 /* And clear out the rest, for good measure.
673 from = bits + (L * (CHAR_ROWS - cut));
674 end = bits + (L * CHAR_ROWS);
679 xi->height -= (cut * ch);
680 mp->real_char_rows -= cut;
683 /* Finally, pull the map indexes back to match the new bits.
685 for (i = 0; i < countof(matrix_encoding); i++)
686 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
687 matrix_encoding[i] -= (cut * CHAR_COLS);
693 load_textures (ModeInfo *mi, Bool flip_p)
695 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
701 /* The Matrix image is 512x598 -- but GL texture sizes must be powers of 2.
702 So we waste some padding rows to round up.
704 xi = image_data_to_ximage (mi->dpy, mi->xgwa.visual,
705 matrix3_png, sizeof(matrix3_png));
708 mp->real_char_rows = CHAR_ROWS;
709 spank_image (mp, xi);
711 if (xi->height != 512 && xi->height != 1024)
713 xi->height = (xi->height < 512 ? 512 : 1024);
714 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
717 fprintf(stderr, "%s: out of memory\n", progname);
722 if (xi->width != 512) abort();
723 if (xi->height != 512 && xi->height != 1024) abort();
725 /* char size in pixels */
726 cw = orig_w / CHAR_COLS;
727 ch = orig_h / CHAR_ROWS;
729 /* char size in ratio of final (padded) texture size */
730 mp->tex_char_width = (GLfloat) cw / xi->width;
731 mp->tex_char_height = (GLfloat) ch / xi->height;
733 /* Flip each character's bits horizontally -- we could also just do this
734 by reversing the texture coordinates on the quads, but on some systems
735 that slows things down a lot.
740 unsigned long buf[100];
741 for (y = 0; y < xi->height; y++)
742 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
744 for (x = 0; x < cw; x++)
745 buf[x] = XGetPixel (xi, xx+x, y);
746 for (x = 0; x < cw; x++)
747 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
751 /* The pixmap is a color image with no transparency. Set the texture's
752 alpha to be the green channel, and set the green channel to be 100%.
755 int rpos, gpos, bpos, apos; /* bitfield positions */
757 /* #### Cherub says that the little-endian case must be taken on MacOSX,
758 or else the colors/alpha are the wrong way around. How can
762 rpos = 24, gpos = 16, bpos = 8, apos = 0;
765 rpos = 0, gpos = 8, bpos = 16, apos = 24;
767 for (y = 0; y < xi->height; y++)
768 for (x = 0; x < xi->width; x++)
770 unsigned long p = XGetPixel (xi, x, y);
771 unsigned char r = (p >> rpos) & 0xFF;
772 unsigned char g = (p >> gpos) & 0xFF;
773 unsigned char b = (p >> bpos) & 0xFF;
776 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
777 XPutPixel (xi, x, y, p);
781 /* Now load the texture into GL.
784 glGenTextures (1, &mp->texture);
786 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
788 /* glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);*/
789 glBindTexture (GL_TEXTURE_2D, mp->texture);
790 check_gl_error ("texture init");
791 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
792 GL_UNSIGNED_BYTE, xi->data);
795 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
796 check_gl_error (buf);
799 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
800 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
802 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
803 faint solid green border around the texture if it is *not* REPEAT!
805 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
806 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
808 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
809 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
810 check_gl_error ("texture param");
817 init_matrix (ModeInfo *mi)
819 matrix_configuration *mp;
820 int wire = MI_IS_WIREFRAME(mi);
829 mp = &mps[MI_SCREEN(mi)];
830 mp->glx_context = init_GL(mi);
832 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
835 mp->glyph_map = matrix_encoding;
836 mp->nglyphs = countof(matrix_encoding);
838 else if (!strcasecmp (mode_str, "dna"))
841 mp->glyph_map = dna_encoding;
842 mp->nglyphs = countof(dna_encoding);
844 else if (!strcasecmp (mode_str, "bin") ||
845 !strcasecmp (mode_str, "binary"))
848 mp->glyph_map = binary_encoding;
849 mp->nglyphs = countof(binary_encoding);
851 else if (!strcasecmp (mode_str, "hex") ||
852 !strcasecmp (mode_str, "hexadecimal"))
855 mp->glyph_map = hex_encoding;
856 mp->nglyphs = countof(hex_encoding);
858 else if (!strcasecmp (mode_str, "dec") ||
859 !strcasecmp (mode_str, "decimal"))
862 mp->glyph_map = decimal_encoding;
863 mp->nglyphs = countof(decimal_encoding);
868 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
873 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
875 glShadeModel(GL_SMOOTH);
877 glDisable(GL_DEPTH_TEST);
878 glDisable(GL_CULL_FACE);
879 glEnable(GL_NORMALIZE);
882 load_textures (mi, flip_p);
884 /* to scale coverage-percent to strips, this number looks about right... */
885 mp->nstrips = (int) (density * 2.2);
886 if (mp->nstrips < 1) mp->nstrips = 1;
887 else if (mp->nstrips > 2000) mp->nstrips = 2000;
890 mp->strips = calloc (mp->nstrips, sizeof(strip));
891 for (i = 0; i < mp->nstrips; i++)
893 strip *s = &mp->strips[i];
896 /* If we start all strips from zero at once, then the first few seconds
897 of the animation are much denser than normal. So instead, set all
898 the initial strips to erase-mode with random starting positions.
899 As these die off at random speeds and are re-created, we'll get a
900 more consistent density. */
902 s->spinner_y = frand(GRID_SIZE);
903 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
906 /* Compute the brightness ramp.
908 for (i = 0; i < WAVE_SIZE; i++)
910 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
911 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
912 j = sin (j); /* j ranges from 0.0 - 1.0 */
913 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
914 mp->brightness_ramp[i] = j;
915 /* printf("%2d %8.2f\n", i, j); */
919 auto_track_init (mi);
926 draw_grid (ModeInfo *mi)
928 if (!MI_IS_WIREFRAME(mi))
930 glDisable(GL_TEXTURE_2D);
937 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
938 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
940 glBegin(GL_LINE_LOOP);
941 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
942 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
943 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
944 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
946 glBegin(GL_LINE_LOOP);
947 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
948 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
949 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
950 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
952 glBegin(GL_LINE_LOOP);
953 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
954 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
955 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
956 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
959 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
960 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
961 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
962 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
963 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
964 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
965 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
966 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
969 if (!MI_IS_WIREFRAME(mi))
971 glEnable(GL_TEXTURE_2D);
979 draw_matrix (ModeInfo *mi)
981 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
982 Display *dpy = MI_DISPLAY(mi);
983 Window window = MI_WINDOW(mi);
986 if (!mp->glx_context)
989 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
991 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
994 glRotatef(current_device_rotation(), 0, 0, 1);
998 glEnable(GL_TEXTURE_2D);
1001 /* Jeff Epler points out:
1002 By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
1003 added to each other, so that a bright glyph with a darker one
1004 in front is a little brighter than the bright glyph alone.
1006 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
1011 glRotatef (mp->view_x, 1, 0, 0);
1012 glRotatef (mp->view_y, 0, 1, 0);
1017 glScalef(0.5, 0.5, 0.5);
1020 glRotatef(-30, 0, 1, 0);
1025 mi->polygon_count = 0;
1027 /* Render (and tick) each strip, starting at the back
1028 (draw the ones farthest from the camera first, to make
1029 the alpha transparency work out right.)
1032 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1033 for (i = 0; i < mp->nstrips; i++)
1034 sorted[i] = &mp->strips[i];
1035 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1037 for (i = 0; i < mp->nstrips; i++)
1039 strip *s = sorted[i];
1051 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1052 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1053 glTexCoord2f (1,1); glVertex3f(15,15,0);
1054 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1060 if (mi->fps_p) do_fps (mi);
1063 glXSwapBuffers(dpy, window);
1066 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)