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 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 "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;
577 if (width > height * 5) { /* tiny window: show middle */
578 height = width * 9/16;
580 h = height / (GLfloat) width;
583 glViewport (0, y, (GLint) width, (GLint) height);
585 glMatrixMode(GL_PROJECTION);
587 gluPerspective (80.0, 1/h, 1.0, 100);
589 glMatrixMode(GL_MODELVIEW);
591 gluLookAt( 0.0, 0.0, 25.0,
595 glClear(GL_COLOR_BUFFER_BIT);
600 matrix_handle_event (ModeInfo *mi, XEvent *event)
602 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
604 if (event->xany.type == ButtonPress &&
605 event->xbutton.button == Button1)
607 mp->button_down_p = True;
610 else if (event->xany.type == ButtonRelease &&
611 event->xbutton.button == Button1)
613 mp->button_down_p = False;
625 union { int i; char c[sizeof(int)]; } u;
632 /* The image with the characters in it is 512x598, meaning that it needs to
633 be copied into a 512x1024 texture. But some machines can't handle textures
634 that large... And it turns out that we aren't using most of the characters
635 in that image anyway, since this program doesn't do anything that makes use
636 of the full range of Latin1 characters. So... this function tosses out the
637 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
638 can then stuff in a 512x512 texture. Voila.
640 If this hack ever grows into something that displays full Latin1 text,
641 well then, Something Else Will Need To Be Done.
643 Since currently GLMatrix does not run textclient / xscreensaver-text,
644 it's not an issue. (XMatrix does that.)
648 spank_image (matrix_configuration *mp, XImage *xi)
650 int ch = xi->height / CHAR_ROWS;
652 unsigned char *bits = (unsigned char *) xi->data;
653 unsigned char *from, *to, *s, *end;
654 int L = xi->bytes_per_line * ch;
657 /* Copy row 12 into 10 (which really means, copy 2 into 0,
658 since texture data is upside down.).
660 to = bits + (L * cut);
667 /* Then, pull all the bits down by 2 rows.
670 from = bits + (L * cut);
671 end = bits + (L * CHAR_ROWS);
676 /* And clear out the rest, for good measure.
678 from = bits + (L * (CHAR_ROWS - cut));
679 end = bits + (L * CHAR_ROWS);
684 xi->height -= (cut * ch);
685 mp->real_char_rows -= cut;
688 /* Finally, pull the map indexes back to match the new bits.
690 for (i = 0; i < countof(matrix_encoding); i++)
691 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
692 matrix_encoding[i] -= (cut * CHAR_COLS);
698 load_textures (ModeInfo *mi, Bool flip_p)
700 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
706 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
707 So we waste some padding rows to round up.
709 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
713 mp->real_char_rows = CHAR_ROWS;
714 spank_image (mp, xi);
716 if (xi->height != 512 && xi->height != 1024)
718 xi->height = (xi->height < 512 ? 512 : 1024);
719 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
722 fprintf(stderr, "%s: out of memory\n", progname);
727 if (xi->width != 512) abort();
728 if (xi->height != 512 && xi->height != 1024) abort();
730 /* char size in pixels */
731 cw = orig_w / CHAR_COLS;
732 ch = orig_h / CHAR_ROWS;
734 /* char size in ratio of final (padded) texture size */
735 mp->tex_char_width = (GLfloat) cw / xi->width;
736 mp->tex_char_height = (GLfloat) ch / xi->height;
738 /* Flip each character's bits horizontally -- we could also just do this
739 by reversing the texture coordinates on the quads, but on some systems
740 that slows things down a lot.
745 unsigned long buf[100];
746 for (y = 0; y < xi->height; y++)
747 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
749 for (x = 0; x < cw; x++)
750 buf[x] = XGetPixel (xi, xx+x, y);
751 for (x = 0; x < cw; x++)
752 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
756 /* The pixmap is a color image with no transparency. Set the texture's
757 alpha to be the green channel, and set the green channel to be 100%.
760 int rpos, gpos, bpos, apos; /* bitfield positions */
762 /* #### Cherub says that the little-endian case must be taken on MacOSX,
763 or else the colors/alpha are the wrong way around. How can
767 rpos = 24, gpos = 16, bpos = 8, apos = 0;
770 rpos = 0, gpos = 8, bpos = 16, apos = 24;
772 for (y = 0; y < xi->height; y++)
773 for (x = 0; x < xi->width; x++)
775 unsigned long p = XGetPixel (xi, x, y);
776 unsigned char r = (p >> rpos) & 0xFF;
777 unsigned char g = (p >> gpos) & 0xFF;
778 unsigned char b = (p >> bpos) & 0xFF;
781 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
782 XPutPixel (xi, x, y, p);
786 /* Now load the texture into GL.
789 glGenTextures (1, &mp->texture);
791 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
793 /* glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);*/
794 glBindTexture (GL_TEXTURE_2D, mp->texture);
795 check_gl_error ("texture init");
796 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
797 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
800 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
801 check_gl_error (buf);
804 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
805 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
807 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
808 faint solid green border around the texture if it is *not* REPEAT!
810 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
811 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
813 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
814 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
815 check_gl_error ("texture param");
822 init_matrix (ModeInfo *mi)
824 matrix_configuration *mp;
825 int wire = MI_IS_WIREFRAME(mi);
834 mp = &mps[MI_SCREEN(mi)];
835 mp->glx_context = init_GL(mi);
837 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
840 mp->glyph_map = matrix_encoding;
841 mp->nglyphs = countof(matrix_encoding);
843 else if (!strcasecmp (mode_str, "dna"))
846 mp->glyph_map = dna_encoding;
847 mp->nglyphs = countof(dna_encoding);
849 else if (!strcasecmp (mode_str, "bin") ||
850 !strcasecmp (mode_str, "binary"))
853 mp->glyph_map = binary_encoding;
854 mp->nglyphs = countof(binary_encoding);
856 else if (!strcasecmp (mode_str, "hex") ||
857 !strcasecmp (mode_str, "hexadecimal"))
860 mp->glyph_map = hex_encoding;
861 mp->nglyphs = countof(hex_encoding);
863 else if (!strcasecmp (mode_str, "dec") ||
864 !strcasecmp (mode_str, "decimal"))
867 mp->glyph_map = decimal_encoding;
868 mp->nglyphs = countof(decimal_encoding);
873 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
878 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
880 glShadeModel(GL_SMOOTH);
882 glDisable(GL_DEPTH_TEST);
883 glDisable(GL_CULL_FACE);
884 glEnable(GL_NORMALIZE);
887 load_textures (mi, flip_p);
889 /* to scale coverage-percent to strips, this number looks about right... */
890 mp->nstrips = (int) (density * 2.2);
891 if (mp->nstrips < 1) mp->nstrips = 1;
892 else if (mp->nstrips > 2000) mp->nstrips = 2000;
895 mp->strips = calloc (mp->nstrips, sizeof(strip));
896 for (i = 0; i < mp->nstrips; i++)
898 strip *s = &mp->strips[i];
901 /* If we start all strips from zero at once, then the first few seconds
902 of the animation are much denser than normal. So instead, set all
903 the initial strips to erase-mode with random starting positions.
904 As these die off at random speeds and are re-created, we'll get a
905 more consistent density. */
907 s->spinner_y = frand(GRID_SIZE);
908 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
911 /* Compute the brightness ramp.
913 for (i = 0; i < WAVE_SIZE; i++)
915 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
916 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
917 j = sin (j); /* j ranges from 0.0 - 1.0 */
918 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
919 mp->brightness_ramp[i] = j;
920 /* printf("%2d %8.2f\n", i, j); */
924 auto_track_init (mi);
931 draw_grid (ModeInfo *mi)
933 if (!MI_IS_WIREFRAME(mi))
935 glDisable(GL_TEXTURE_2D);
942 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
943 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
945 glBegin(GL_LINE_LOOP);
946 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
947 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
948 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
949 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
951 glBegin(GL_LINE_LOOP);
952 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
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);
957 glBegin(GL_LINE_LOOP);
958 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);
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);
968 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
969 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
970 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
971 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
974 if (!MI_IS_WIREFRAME(mi))
976 glEnable(GL_TEXTURE_2D);
984 draw_matrix (ModeInfo *mi)
986 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
987 Display *dpy = MI_DISPLAY(mi);
988 Window window = MI_WINDOW(mi);
991 if (!mp->glx_context)
994 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
996 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
999 glRotatef(current_device_rotation(), 0, 0, 1);
1003 glEnable(GL_TEXTURE_2D);
1006 /* Jeff Epler points out:
1007 By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
1008 added to each other, so that a bright glyph with a darker one
1009 in front is a little brighter than the bright glyph alone.
1011 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
1016 glRotatef (mp->view_x, 1, 0, 0);
1017 glRotatef (mp->view_y, 0, 1, 0);
1022 glScalef(0.5, 0.5, 0.5);
1025 glRotatef(-30, 0, 1, 0);
1030 mi->polygon_count = 0;
1032 /* Render (and tick) each strip, starting at the back
1033 (draw the ones farthest from the camera first, to make
1034 the alpha transparency work out right.)
1037 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1038 for (i = 0; i < mp->nstrips; i++)
1039 sorted[i] = &mp->strips[i];
1040 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1042 for (i = 0; i < mp->nstrips; i++)
1044 strip *s = sorted[i];
1056 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1057 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1058 glTexCoord2f (1,1); glVertex3f(15,15,0);
1059 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1065 if (mi->fps_p) do_fps (mi);
1068 glXSwapBuffers(dpy, window);
1071 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)