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.
636 Since currently GLMatrix does not run textclient / xscreensaver-text,
637 it's not an issue. (XMatrix does that.)
641 spank_image (matrix_configuration *mp, XImage *xi)
643 int ch = xi->height / CHAR_ROWS;
645 unsigned char *bits = (unsigned char *) xi->data;
646 unsigned char *from, *to, *s, *end;
647 int L = xi->bytes_per_line * ch;
650 /* Copy row 12 into 10 (which really means, copy 2 into 0,
651 since texture data is upside down.).
653 to = bits + (L * cut);
660 /* Then, pull all the bits down by 2 rows.
663 from = bits + (L * cut);
664 end = bits + (L * CHAR_ROWS);
669 /* And clear out the rest, for good measure.
671 from = bits + (L * (CHAR_ROWS - cut));
672 end = bits + (L * CHAR_ROWS);
677 xi->height -= (cut * ch);
678 mp->real_char_rows -= cut;
681 /* Finally, pull the map indexes back to match the new bits.
683 for (i = 0; i < countof(matrix_encoding); i++)
684 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
685 matrix_encoding[i] -= (cut * CHAR_COLS);
691 load_textures (ModeInfo *mi, Bool flip_p)
693 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
699 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
700 So we waste some padding rows to round up.
702 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
706 mp->real_char_rows = CHAR_ROWS;
707 spank_image (mp, xi);
709 if (xi->height != 512 && xi->height != 1024)
711 xi->height = (xi->height < 512 ? 512 : 1024);
712 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
715 fprintf(stderr, "%s: out of memory\n", progname);
720 if (xi->width != 512) abort();
721 if (xi->height != 512 && xi->height != 1024) abort();
723 /* char size in pixels */
724 cw = orig_w / CHAR_COLS;
725 ch = orig_h / CHAR_ROWS;
727 /* char size in ratio of final (padded) texture size */
728 mp->tex_char_width = (GLfloat) cw / xi->width;
729 mp->tex_char_height = (GLfloat) ch / xi->height;
731 /* Flip each character's bits horizontally -- we could also just do this
732 by reversing the texture coordinates on the quads, but on some systems
733 that slows things down a lot.
738 unsigned long buf[100];
739 for (y = 0; y < xi->height; y++)
740 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
742 for (x = 0; x < cw; x++)
743 buf[x] = XGetPixel (xi, xx+x, y);
744 for (x = 0; x < cw; x++)
745 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
749 /* The pixmap is a color image with no transparency. Set the texture's
750 alpha to be the green channel, and set the green channel to be 100%.
753 int rpos, gpos, bpos, apos; /* bitfield positions */
755 /* #### Cherub says that the little-endian case must be taken on MacOSX,
756 or else the colors/alpha are the wrong way around. How can
760 rpos = 24, gpos = 16, bpos = 8, apos = 0;
763 rpos = 0, gpos = 8, bpos = 16, apos = 24;
765 for (y = 0; y < xi->height; y++)
766 for (x = 0; x < xi->width; x++)
768 unsigned long p = XGetPixel (xi, x, y);
769 unsigned char r = (p >> rpos) & 0xFF;
770 unsigned char g = (p >> gpos) & 0xFF;
771 unsigned char b = (p >> bpos) & 0xFF;
774 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
775 XPutPixel (xi, x, y, p);
779 /* Now load the texture into GL.
782 glGenTextures (1, &mp->texture);
784 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
786 /* glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);*/
787 glBindTexture (GL_TEXTURE_2D, mp->texture);
788 check_gl_error ("texture init");
789 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
790 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
793 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
794 check_gl_error (buf);
797 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
798 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
800 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
801 faint solid green border around the texture if it is *not* REPEAT!
803 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
804 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
806 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
807 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
808 check_gl_error ("texture param");
815 init_matrix (ModeInfo *mi)
817 matrix_configuration *mp;
818 int wire = MI_IS_WIREFRAME(mi);
825 MI_INIT (mi, mps, NULL);
827 mp = &mps[MI_SCREEN(mi)];
828 mp->glx_context = init_GL(mi);
830 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
833 mp->glyph_map = matrix_encoding;
834 mp->nglyphs = countof(matrix_encoding);
836 else if (!strcasecmp (mode_str, "dna"))
839 mp->glyph_map = dna_encoding;
840 mp->nglyphs = countof(dna_encoding);
842 else if (!strcasecmp (mode_str, "bin") ||
843 !strcasecmp (mode_str, "binary"))
846 mp->glyph_map = binary_encoding;
847 mp->nglyphs = countof(binary_encoding);
849 else if (!strcasecmp (mode_str, "hex") ||
850 !strcasecmp (mode_str, "hexadecimal"))
853 mp->glyph_map = hex_encoding;
854 mp->nglyphs = countof(hex_encoding);
856 else if (!strcasecmp (mode_str, "dec") ||
857 !strcasecmp (mode_str, "decimal"))
860 mp->glyph_map = decimal_encoding;
861 mp->nglyphs = countof(decimal_encoding);
866 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
871 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
873 glShadeModel(GL_SMOOTH);
875 glDisable(GL_DEPTH_TEST);
876 glDisable(GL_CULL_FACE);
877 glEnable(GL_NORMALIZE);
880 load_textures (mi, flip_p);
882 /* to scale coverage-percent to strips, this number looks about right... */
883 mp->nstrips = (int) (density * 2.2);
884 if (mp->nstrips < 1) mp->nstrips = 1;
885 else if (mp->nstrips > 2000) mp->nstrips = 2000;
888 mp->strips = calloc (mp->nstrips, sizeof(strip));
889 for (i = 0; i < mp->nstrips; i++)
891 strip *s = &mp->strips[i];
894 /* If we start all strips from zero at once, then the first few seconds
895 of the animation are much denser than normal. So instead, set all
896 the initial strips to erase-mode with random starting positions.
897 As these die off at random speeds and are re-created, we'll get a
898 more consistent density. */
900 s->spinner_y = frand(GRID_SIZE);
901 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
904 /* Compute the brightness ramp.
906 for (i = 0; i < WAVE_SIZE; i++)
908 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
909 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
910 j = sin (j); /* j ranges from 0.0 - 1.0 */
911 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
912 mp->brightness_ramp[i] = j;
913 /* printf("%2d %8.2f\n", i, j); */
917 auto_track_init (mi);
924 draw_grid (ModeInfo *mi)
926 if (!MI_IS_WIREFRAME(mi))
928 glDisable(GL_TEXTURE_2D);
935 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
936 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
938 glBegin(GL_LINE_LOOP);
939 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
940 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
941 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
942 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
944 glBegin(GL_LINE_LOOP);
945 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
946 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
947 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
948 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
950 glBegin(GL_LINE_LOOP);
951 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
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);
957 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
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);
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);
967 if (!MI_IS_WIREFRAME(mi))
969 glEnable(GL_TEXTURE_2D);
977 draw_matrix (ModeInfo *mi)
979 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
980 Display *dpy = MI_DISPLAY(mi);
981 Window window = MI_WINDOW(mi);
984 if (!mp->glx_context)
987 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
989 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
992 glRotatef(current_device_rotation(), 0, 0, 1);
996 glEnable(GL_TEXTURE_2D);
999 /* Jeff Epler points out:
1000 By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
1001 added to each other, so that a bright glyph with a darker one
1002 in front is a little brighter than the bright glyph alone.
1004 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
1009 glRotatef (mp->view_x, 1, 0, 0);
1010 glRotatef (mp->view_y, 0, 1, 0);
1015 glScalef(0.5, 0.5, 0.5);
1018 glRotatef(-30, 0, 1, 0);
1023 mi->polygon_count = 0;
1025 /* Render (and tick) each strip, starting at the back
1026 (draw the ones farthest from the camera first, to make
1027 the alpha transparency work out right.)
1030 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1031 for (i = 0; i < mp->nstrips; i++)
1032 sorted[i] = &mp->strips[i];
1033 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1035 for (i = 0; i < mp->nstrips; i++)
1037 strip *s = sorted[i];
1049 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1050 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1051 glTexCoord2f (1,1); glVertex3f(15,15,0);
1052 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1058 if (mi->fps_p) do_fps (mi);
1061 glXSwapBuffers(dpy, window);
1064 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)