1 /* glmatrix, Copyright (c) 2003, 2004 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 refresh_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 "xpm-ximage.h"
35 __extension__ /* don't warn about "string length is greater than the length
36 ISO C89 compilers are required to support" when including
37 the following XPM file... */
39 #include "../images/matrix3.xpm"
41 #ifdef USE_GL /* whole file */
44 #define DEF_SPEED "1.0"
45 #define DEF_DENSITY "20"
46 #define DEF_CLOCK "False"
47 #define DEF_FOG "True"
48 #define DEF_WAVES "True"
49 #define DEF_ROTATE "True"
50 #define DEF_TEXTURE "True"
51 #define DEF_MODE "Matrix"
52 #define DEF_TIMEFMT " %l%M%p "
58 static const int matrix_encoding[] = {
59 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
61 192, 193, 194, 195, 196, 197, 198, 199,
62 200, 201, 202, 203, 204, 205, 206, 207
64 160, 161, 162, 163, 164, 165, 166, 167,
65 168, 169, 170, 171, 172, 173, 174, 175
68 static const int decimal_encoding[] = {
69 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
70 static const int hex_encoding[] = {
71 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 };
72 static const int binary_encoding[] = { 16, 17 };
73 static const int dna_encoding[] = { 33, 35, 39, 52 };
75 static const unsigned char char_map[256] = {
76 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */
77 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */
78 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
79 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
80 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
81 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
82 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
83 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
84 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */
85 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */
86 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
87 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
88 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
89 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
91 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
92 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
93 #else /* see spank_image() */
94 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */
95 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */
99 #define CURSOR_GLYPH 97
103 #define GRID_SIZE 70 /* width and height of the arena */
104 #define GRID_DEPTH 35 /* depth of the arena */
105 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
106 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
108 static const struct { GLfloat x, y; } nice_views[] = {
110 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
111 { 0, 20 }, /* every now and then we switch to a new one. */
112 { 25, 0 }, /* (but we only use the first one at start-up.) */
121 { 0, 0 }, /* prefer these */
130 GLfloat x, y, z; /* position of strip */
131 GLfloat dx, dy, dz; /* velocity of strip */
133 Bool erasing_p; /* Whether this strip is on its way out. */
135 int spinner_glyph; /* the bottommost glyph -- the feeder */
136 GLfloat spinner_y; /* where on the strip the bottom glyph is */
137 GLfloat spinner_speed; /* how fast the bottom glyph drops */
139 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
140 revealed by the dropping spinner.
141 0 means no glyph; negative means "spinner".
142 If non-zero, real value is abs(G)-1. */
144 Bool highlight[GRID_SIZE];
145 /* some glyphs may be highlighted */
147 int spin_speed; /* Rotate all spinners every this-many frames */
148 int spin_tick; /* frame counter */
150 int wave_position; /* Waves of brightness wash down the strip. */
151 int wave_speed; /* every this-many frames. */
152 int wave_tick; /* frame counter. */
158 GLXContext *glx_context;
163 const int *glyph_map;
165 GLfloat tex_char_width, tex_char_height;
167 /* auto-tracking direction of view */
168 int last_view, target_view;
169 GLfloat view_x, view_y;
170 int view_steps, view_tick;
171 Bool auto_tracking_p;
175 GLfloat brightness_ramp[WAVE_SIZE];
177 } matrix_configuration;
179 static matrix_configuration *mps = NULL;
181 static GLfloat speed;
182 static GLfloat density;
183 static Bool do_clock;
184 static char *timefmt;
186 static Bool do_waves;
187 static Bool do_rotate;
188 static Bool do_texture;
189 static char *mode_str;
191 static XrmOptionDescRec opts[] = {
192 { "-speed", ".speed", XrmoptionSepArg, 0 },
193 { "-density", ".density", XrmoptionSepArg, 0 },
194 { "-mode", ".mode", XrmoptionSepArg, 0 },
195 { "-binary", ".mode", XrmoptionNoArg, "binary" },
196 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
197 { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
198 { "-dna", ".mode", XrmoptionNoArg, "dna" },
199 { "-clock", ".clock", XrmoptionNoArg, "True" },
200 { "+clock", ".clock", XrmoptionNoArg, "False" },
201 { "-timefmt", ".timefmt", XrmoptionSepArg, 0 },
202 { "-fog", ".fog", XrmoptionNoArg, "True" },
203 { "+fog", ".fog", XrmoptionNoArg, "False" },
204 { "-waves", ".waves", XrmoptionNoArg, "True" },
205 { "+waves", ".waves", XrmoptionNoArg, "False" },
206 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
207 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
208 {"-texture", ".texture", XrmoptionNoArg, "True" },
209 {"+texture", ".texture", XrmoptionNoArg, "False" },
212 static argtype vars[] = {
213 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
214 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
215 {&density, "density", "Density", DEF_DENSITY, t_Float},
216 {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool},
217 {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String},
218 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
219 {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
220 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
221 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
224 ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
227 /* Re-randomize the state of one strip.
230 reset_strip (ModeInfo *mi, strip *s)
232 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
234 Bool time_displayed_p = False; /* never display time twice in one strip */
236 memset (s, 0, sizeof(*s));
237 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
238 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
239 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
243 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
245 s->dz = (BELLRAND(0.02) * speed);
247 s->spinner_speed = (BELLRAND(0.3) * speed);
249 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
252 s->wave_position = 0;
253 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
256 for (i = 0; i < GRID_SIZE; i++)
259 (i < GRID_SIZE-5) && /* display approx. once per 5 strips */
260 !(random() % (GRID_SIZE-5)*5))
264 time_t now = time ((time_t *) 0);
265 struct tm *tm = localtime (&now);
266 strftime (text, sizeof(text)-1, timefmt, tm);
268 /* render time into the strip */
269 for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
271 s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
272 s->highlight[i] = True;
275 time_displayed_p = True;
279 int draw_p = (random() % 7);
280 int spin_p = (draw_p && !(random() % 20));
282 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
286 s->highlight[i] = False;
289 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
293 /* Animate the strip one step. Reset if it has reached the bottom.
296 tick_strip (ModeInfo *mi, strip *s)
298 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
301 if (mp->button_down_p)
308 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
314 s->spinner_y += s->spinner_speed;
315 if (s->spinner_y >= GRID_SIZE)
326 s->spinner_speed /= 2; /* erase it slower than we drew it */
330 /* Spin the spinners. */
332 if (s->spin_tick > s->spin_speed)
335 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
336 for (i = 0; i < GRID_SIZE; i++)
337 if (s->glyphs[i] < 0)
339 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
340 if (! (random() % 800)) /* sometimes they stop spinning */
341 s->glyphs[i] = -s->glyphs[i];
345 /* Move the color (brightness) wave. */
347 if (s->wave_tick > s->wave_speed)
351 if (s->wave_position >= WAVE_SIZE)
352 s->wave_position = 0;
357 /* Draw a single character at the given position and brightness.
360 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
361 GLfloat x, GLfloat y, GLfloat z,
364 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
365 int wire = MI_IS_WIREFRAME(mi);
366 GLfloat w = mp->tex_char_width;
367 GLfloat h = mp->tex_char_height;
368 GLfloat cx = 0, cy = 0;
370 Bool spinner_p = (glyph < 0);
372 if (glyph == 0) abort();
373 if (glyph < 0) glyph = -glyph;
386 int ccx = ((glyph - 1) % CHAR_COLS);
387 int ccy = ((glyph - 1) / CHAR_COLS);
390 cy = (mp->real_char_rows - ccy - 1) * h;
395 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
396 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
397 brightness *= depth; /* so no row goes all black. */
407 if (!do_texture && !spinner_p)
414 /* If the glyph is very close to the screen (meaning it is very large,
415 and is about to splash into the screen and vanish) then start fading
416 it out, proportional to how close to the glass it is.
418 if (z > GRID_DEPTH/2)
420 GLfloat ratio = ((z - GRID_DEPTH/2) /
421 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
422 int i = ratio * WAVE_SIZE;
425 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
427 a *= mp->brightness_ramp[i];
433 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
434 glNormal3f (0, 0, 1);
435 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
436 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
437 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
438 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
441 if (wire && spinner_p)
444 glVertex3f (x, y, z);
445 glVertex3f (x+S, y+S, z);
446 glVertex3f (x, y+S, z);
447 glVertex3f (x+S, y, z);
455 /* Draw all the visible glyphs in the strip.
458 draw_strip (ModeInfo *mi, strip *s)
460 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
462 for (i = 0; i < GRID_SIZE; i++)
464 int g = s->glyphs[i];
465 Bool below_p = (s->spinner_y >= i);
470 if (g && below_p) /* don't draw cells below the spinner */
477 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
479 brightness = mp->brightness_ramp[j];
482 draw_glyph (mi, g, s->highlight[i],
483 s->x, s->y - i, s->z, brightness);
488 draw_glyph (mi, s->spinner_glyph, False,
489 s->x, s->y - s->spinner_y, s->z, 1.0);
493 /* qsort comparator for sorting strips by z position */
495 cmp_strips (const void *aa, const void *bb)
497 const strip *a = *(strip **) aa;
498 const strip *b = *(strip **) bb;
499 return ((int) (a->z * 10000) -
500 (int) (b->z * 10000));
508 auto_track_init (ModeInfo *mi)
510 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
513 mp->view_x = nice_views[mp->last_view].x;
514 mp->view_y = nice_views[mp->last_view].y;
515 mp->view_steps = 100;
517 mp->auto_tracking_p = False;
522 auto_track (ModeInfo *mi)
524 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
528 if (mp->button_down_p)
531 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
532 if (! mp->auto_tracking_p)
534 if (++mp->track_tick < 20/speed) return;
536 if (! (random() % 20))
537 mp->auto_tracking_p = True;
544 GLfloat ox = nice_views[mp->last_view].x;
545 GLfloat oy = nice_views[mp->last_view].y;
546 GLfloat tx = nice_views[mp->target_view].x;
547 GLfloat ty = nice_views[mp->target_view].y;
549 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
551 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
553 mp->view_x = (ox + ((tx - ox) * th));
554 mp->view_y = (oy + ((ty - oy) * th));
557 if (mp->view_tick >= mp->view_steps)
560 mp->view_steps = (350.0 / speed);
561 mp->last_view = mp->target_view;
562 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
563 mp->auto_tracking_p = False;
569 /* Window management, etc
572 reshape_matrix (ModeInfo *mi, int width, int height)
574 GLfloat h = (GLfloat) height / (GLfloat) width;
576 glViewport (0, 0, (GLint) width, (GLint) height);
578 glMatrixMode(GL_PROJECTION);
580 gluPerspective (80.0, 1/h, 1.0, 100);
582 glMatrixMode(GL_MODELVIEW);
584 gluLookAt( 0.0, 0.0, 25.0,
588 glClear(GL_COLOR_BUFFER_BIT);
593 matrix_handle_event (ModeInfo *mi, XEvent *event)
595 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
597 if (event->xany.type == ButtonPress &&
598 event->xbutton.button == Button1)
600 mp->button_down_p = True;
603 else if (event->xany.type == ButtonRelease &&
604 event->xbutton.button == Button1)
606 mp->button_down_p = False;
618 union { int i; char c[sizeof(int)]; } u;
625 /* The image with the characters in it is 512x598, meaning that it needs to
626 be copied into a 512x1024 texture. But some machines can't handle textures
627 that large... And it turns out that we aren't using most of the characters
628 in that image anyway, since this program doesn't do anything that makes use
629 of the full range of Latin1 characters. So... this function tosses out the
630 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
631 can then stuff in a 512x512 texture. Voila.
633 If this hack ever grows into something that displays full Latin1 text,
634 well then, Something Else Will Need To Be Done.
637 spank_image (matrix_configuration *mp, XImage *xi)
639 int ch = xi->height / CHAR_ROWS;
641 unsigned char *bits = (unsigned char *) xi->data;
642 unsigned char *from, *to, *s, *end;
643 int L = xi->bytes_per_line * ch;
646 /* Copy row 12 into 10 (which really means, copy 2 into 0,
647 since texture data is upside down.).
649 to = bits + (L * cut);
656 /* Then, pull all the bits down by 2 rows.
659 from = bits + (L * cut);
660 end = bits + (L * CHAR_ROWS);
665 /* And clear out the rest, for good measure.
667 from = bits + (L * (CHAR_ROWS - cut));
668 end = bits + (L * CHAR_ROWS);
673 xi->height -= (cut * ch);
674 mp->real_char_rows -= cut;
677 /* Finally, pull the map indexes back to match the new bits.
679 for (i = 0; i < countof(matrix_encoding); i++)
680 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
681 matrix_encoding[i] -= (cut * CHAR_COLS);
687 load_textures (ModeInfo *mi, Bool flip_p)
689 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
695 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
696 So we waste some padding rows to round up.
698 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
702 mp->real_char_rows = CHAR_ROWS;
703 spank_image (mp, xi);
705 if (xi->height != 512 && xi->height != 1024)
707 xi->height = (xi->height < 512 ? 512 : 1024);
708 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
711 fprintf(stderr, "%s: out of memory\n", progname);
716 if (xi->width != 512) abort();
717 if (xi->height != 512 && xi->height != 1024) abort();
719 /* char size in pixels */
720 cw = orig_w / CHAR_COLS;
721 ch = orig_h / CHAR_ROWS;
723 /* char size in ratio of final (padded) texture size */
724 mp->tex_char_width = (GLfloat) cw / xi->width;
725 mp->tex_char_height = (GLfloat) ch / xi->height;
727 /* Flip each character's bits horizontally -- we could also just do this
728 by reversing the texture coordinates on the quads, but on some systems
729 that slows things down a lot.
734 unsigned long buf[100];
735 for (y = 0; y < xi->height; y++)
736 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
738 for (x = 0; x < cw; x++)
739 buf[x] = XGetPixel (xi, xx+x, y);
740 for (x = 0; x < cw; x++)
741 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
745 /* The pixmap is a color image with no transparency. Set the texture's
746 alpha to be the green channel, and set the green channel to be 100%.
749 int rpos, gpos, bpos, apos; /* bitfield positions */
751 /* #### Cherub says that the little-endian case must be taken on MacOSX,
752 or else the colors/alpha are the wrong way around. How can
756 rpos = 24, gpos = 16, bpos = 8, apos = 0;
759 rpos = 0, gpos = 8, bpos = 16, apos = 24;
761 for (y = 0; y < xi->height; y++)
762 for (x = 0; x < xi->width; x++)
764 unsigned long p = XGetPixel (xi, x, y);
765 unsigned char r = (p >> rpos) & 0xFF;
766 unsigned char g = (p >> gpos) & 0xFF;
767 unsigned char b = (p >> bpos) & 0xFF;
770 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
771 XPutPixel (xi, x, y, p);
775 /* Now load the texture into GL.
778 glGenTextures (1, &mp->texture);
780 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
782 /* glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);*/
783 glBindTexture (GL_TEXTURE_2D, mp->texture);
784 check_gl_error ("texture init");
785 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
786 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
789 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
790 check_gl_error (buf);
793 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
794 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
796 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
797 faint solid green border around the texture if it is *not* REPEAT!
799 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
800 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
802 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
803 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
804 check_gl_error ("texture param");
811 init_matrix (ModeInfo *mi)
813 matrix_configuration *mp;
814 int wire = MI_IS_WIREFRAME(mi);
822 mps = (matrix_configuration *)
823 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
825 fprintf(stderr, "%s: out of memory\n", progname);
830 mp = &mps[MI_SCREEN(mi)];
831 mp->glx_context = init_GL(mi);
833 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
836 mp->glyph_map = matrix_encoding;
837 mp->nglyphs = countof(matrix_encoding);
839 else if (!strcasecmp (mode_str, "dna"))
842 mp->glyph_map = dna_encoding;
843 mp->nglyphs = countof(dna_encoding);
845 else if (!strcasecmp (mode_str, "bin") ||
846 !strcasecmp (mode_str, "binary"))
849 mp->glyph_map = binary_encoding;
850 mp->nglyphs = countof(binary_encoding);
852 else if (!strcasecmp (mode_str, "hex") ||
853 !strcasecmp (mode_str, "hexadecimal"))
856 mp->glyph_map = hex_encoding;
857 mp->nglyphs = countof(hex_encoding);
859 else if (!strcasecmp (mode_str, "dec") ||
860 !strcasecmp (mode_str, "decimal"))
863 mp->glyph_map = decimal_encoding;
864 mp->nglyphs = countof(decimal_encoding);
869 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
874 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
876 glShadeModel(GL_SMOOTH);
878 glDisable(GL_DEPTH_TEST);
879 glDisable(GL_CULL_FACE);
880 glEnable(GL_NORMALIZE);
883 load_textures (mi, flip_p);
885 /* to scale coverage-percent to strips, this number looks about right... */
886 mp->nstrips = (int) (density * 2.2);
887 if (mp->nstrips < 1) mp->nstrips = 1;
888 else if (mp->nstrips > 2000) mp->nstrips = 2000;
891 mp->strips = calloc (mp->nstrips, sizeof(strip));
892 for (i = 0; i < mp->nstrips; i++)
894 strip *s = &mp->strips[i];
897 /* If we start all strips from zero at once, then the first few seconds
898 of the animation are much denser than normal. So instead, set all
899 the initial strips to erase-mode with random starting positions.
900 As these die off at random speeds and are re-created, we'll get a
901 more consistent density. */
903 s->spinner_y = frand(GRID_SIZE);
904 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
907 /* Compute the brightness ramp.
909 for (i = 0; i < WAVE_SIZE; i++)
911 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
912 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
913 j = sin (j); /* j ranges from 0.0 - 1.0 */
914 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
915 mp->brightness_ramp[i] = j;
916 /* printf("%2d %8.2f\n", i, j); */
920 auto_track_init (mi);
927 draw_grid (ModeInfo *mi)
929 if (!MI_IS_WIREFRAME(mi))
931 glDisable(GL_TEXTURE_2D);
938 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
939 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
941 glBegin(GL_LINE_LOOP);
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);
945 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
947 glBegin(GL_LINE_LOOP);
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);
951 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
953 glBegin(GL_LINE_LOOP);
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);
957 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);
967 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
970 if (!MI_IS_WIREFRAME(mi))
972 glEnable(GL_TEXTURE_2D);
980 draw_matrix (ModeInfo *mi)
982 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
983 Display *dpy = MI_DISPLAY(mi);
984 Window window = MI_WINDOW(mi);
987 if (!mp->glx_context)
990 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
992 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
995 glRotatef(current_device_rotation(), 0, 0, 1);
999 glEnable(GL_TEXTURE_2D);
1002 /* Jeff Epler points out:
1003 By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
1004 added to each other, so that a bright glyph with a darker one
1005 in front is a little brighter than the bright glyph alone.
1007 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
1012 glRotatef (mp->view_x, 1, 0, 0);
1013 glRotatef (mp->view_y, 0, 1, 0);
1018 glScalef(0.5, 0.5, 0.5);
1021 glRotatef(-30, 0, 1, 0);
1026 mi->polygon_count = 0;
1028 /* Render (and tick) each strip, starting at the back
1029 (draw the ones farthest from the camera first, to make
1030 the alpha transparency work out right.)
1033 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1034 for (i = 0; i < mp->nstrips; i++)
1035 sorted[i] = &mp->strips[i];
1036 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1038 for (i = 0; i < mp->nstrips; i++)
1040 strip *s = sorted[i];
1052 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1053 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1054 glTexCoord2f (1,1); glVertex3f(15,15,0);
1055 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1061 if (mi->fps_p) do_fps (mi);
1064 glXSwapBuffers(dpy, window);
1067 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)