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);
781 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
782 glBindTexture (GL_TEXTURE_2D, mp->texture);
783 check_gl_error ("texture init");
784 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
785 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
788 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
789 check_gl_error (buf);
792 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
793 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
795 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
796 faint solid green border around the texture if it is *not* REPEAT!
798 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
799 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
801 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
802 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
803 check_gl_error ("texture param");
810 init_matrix (ModeInfo *mi)
812 matrix_configuration *mp;
813 int wire = MI_IS_WIREFRAME(mi);
821 mps = (matrix_configuration *)
822 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
824 fprintf(stderr, "%s: out of memory\n", progname);
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);
883 load_textures (mi, flip_p);
884 glEnable(GL_TEXTURE_2D);
887 /* Jeff Epler points out:
888 By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
889 added to each other, so that a bright glyph with a darker one
890 in front is a little brighter than the bright glyph alone.
892 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
895 /* to scale coverage-percent to strips, this number looks about right... */
896 mp->nstrips = (int) (density * 2.2);
897 if (mp->nstrips < 1) mp->nstrips = 1;
898 else if (mp->nstrips > 2000) mp->nstrips = 2000;
901 mp->strips = calloc (mp->nstrips, sizeof(strip));
902 for (i = 0; i < mp->nstrips; i++)
904 strip *s = &mp->strips[i];
907 /* If we start all strips from zero at once, then the first few seconds
908 of the animation are much denser than normal. So instead, set all
909 the initial strips to erase-mode with random starting positions.
910 As these die off at random speeds and are re-created, we'll get a
911 more consistent density. */
913 s->spinner_y = frand(GRID_SIZE);
914 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
917 /* Compute the brightness ramp.
919 for (i = 0; i < WAVE_SIZE; i++)
921 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
922 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
923 j = sin (j); /* j ranges from 0.0 - 1.0 */
924 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
925 mp->brightness_ramp[i] = j;
926 /* printf("%2d %8.2f\n", i, j); */
930 auto_track_init (mi);
937 draw_grid (ModeInfo *mi)
939 if (!MI_IS_WIREFRAME(mi))
941 glDisable(GL_TEXTURE_2D);
948 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
949 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
951 glBegin(GL_LINE_LOOP);
952 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
953 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
954 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
955 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
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);
963 glBegin(GL_LINE_LOOP);
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 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
971 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
972 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
973 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
974 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
975 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
976 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
977 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
980 if (!MI_IS_WIREFRAME(mi))
982 glEnable(GL_TEXTURE_2D);
990 draw_matrix (ModeInfo *mi)
992 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
993 Display *dpy = MI_DISPLAY(mi);
994 Window window = MI_WINDOW(mi);
997 if (!mp->glx_context)
1000 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
1002 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1008 glRotatef (mp->view_x, 1, 0, 0);
1009 glRotatef (mp->view_y, 0, 1, 0);
1014 glScalef(0.5, 0.5, 0.5);
1017 glRotatef(-30, 0, 1, 0);
1022 mi->polygon_count = 0;
1024 /* Render (and tick) each strip, starting at the back
1025 (draw the ones farthest from the camera first, to make
1026 the alpha transparency work out right.)
1029 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1030 for (i = 0; i < mp->nstrips; i++)
1031 sorted[i] = &mp->strips[i];
1032 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1034 for (i = 0; i < mp->nstrips; i++)
1036 strip *s = sorted[i];
1048 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1049 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1050 glTexCoord2f (1,1); glVertex3f(15,15,0);
1051 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1057 if (mi->fps_p) do_fps (mi);
1060 glXSwapBuffers(dpy, window);
1063 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)