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 "
60 static const int matrix_encoding[] = {
61 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
63 192, 193, 194, 195, 196, 197, 198, 199,
64 200, 201, 202, 203, 204, 205, 206, 207
66 160, 161, 162, 163, 164, 165, 166, 167,
67 168, 169, 170, 171, 172, 173, 174, 175
70 static const int decimal_encoding[] = {
71 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
72 static const int hex_encoding[] = {
73 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 };
74 static const int binary_encoding[] = { 16, 17 };
75 static const int dna_encoding[] = { 33, 35, 39, 52 };
77 static const unsigned char char_map[256] = {
78 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */
79 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */
80 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
81 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
82 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
83 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
84 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
85 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
86 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */
87 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */
88 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
89 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
90 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
91 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
93 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
94 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
95 #else /* see spank_image() */
96 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */
97 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */
101 #define CURSOR_GLYPH 97
105 #define GRID_SIZE 70 /* width and height of the arena */
106 #define GRID_DEPTH 35 /* depth of the arena */
107 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
108 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
110 static const struct { GLfloat x, y; } nice_views[] = {
112 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
113 { 0, 20 }, /* every now and then we switch to a new one. */
114 { 25, 0 }, /* (but we only use the first one at start-up.) */
123 { 0, 0 }, /* prefer these */
132 GLfloat x, y, z; /* position of strip */
133 GLfloat dx, dy, dz; /* velocity of strip */
135 Bool erasing_p; /* Whether this strip is on its way out. */
137 int spinner_glyph; /* the bottommost glyph -- the feeder */
138 GLfloat spinner_y; /* where on the strip the bottom glyph is */
139 GLfloat spinner_speed; /* how fast the bottom glyph drops */
141 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
142 revealed by the dropping spinner.
143 0 means no glyph; negative means "spinner".
144 If non-zero, real value is abs(G)-1. */
146 Bool highlight[GRID_SIZE];
147 /* some glyphs may be highlighted */
149 int spin_speed; /* Rotate all spinners every this-many frames */
150 int spin_tick; /* frame counter */
152 int wave_position; /* Waves of brightness wash down the strip. */
153 int wave_speed; /* every this-many frames. */
154 int wave_tick; /* frame counter. */
160 GLXContext *glx_context;
165 const int *glyph_map;
167 GLfloat tex_char_width, tex_char_height;
169 /* auto-tracking direction of view */
170 int last_view, target_view;
171 GLfloat view_x, view_y;
172 int view_steps, view_tick;
173 Bool auto_tracking_p;
177 GLfloat brightness_ramp[WAVE_SIZE];
179 } matrix_configuration;
181 static matrix_configuration *mps = NULL;
183 static GLfloat speed;
184 static GLfloat density;
185 static Bool do_clock;
186 static char *timefmt;
188 static Bool do_waves;
189 static Bool do_rotate;
190 static Bool do_texture;
191 static char *mode_str;
193 static XrmOptionDescRec opts[] = {
194 { "-speed", ".speed", XrmoptionSepArg, 0 },
195 { "-density", ".density", XrmoptionSepArg, 0 },
196 { "-mode", ".mode", XrmoptionSepArg, 0 },
197 { "-binary", ".mode", XrmoptionNoArg, "binary" },
198 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
199 { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
200 { "-dna", ".mode", XrmoptionNoArg, "dna" },
201 { "-clock", ".clock", XrmoptionNoArg, "True" },
202 { "+clock", ".clock", XrmoptionNoArg, "False" },
203 { "-timefmt", ".timefmt", XrmoptionSepArg, 0 },
204 { "-fog", ".fog", XrmoptionNoArg, "True" },
205 { "+fog", ".fog", XrmoptionNoArg, "False" },
206 { "-waves", ".waves", XrmoptionNoArg, "True" },
207 { "+waves", ".waves", XrmoptionNoArg, "False" },
208 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
209 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
210 {"-texture", ".texture", XrmoptionNoArg, "True" },
211 {"+texture", ".texture", XrmoptionNoArg, "False" },
214 static argtype vars[] = {
215 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
216 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
217 {&density, "density", "Density", DEF_DENSITY, t_Float},
218 {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool},
219 {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String},
220 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
221 {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
222 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
223 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
226 ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
229 /* Re-randomize the state of one strip.
232 reset_strip (ModeInfo *mi, strip *s)
234 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
236 Bool time_displayed_p = False; /* never display time twice in one strip */
238 memset (s, 0, sizeof(*s));
239 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
240 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
241 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
245 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
247 s->dz = (BELLRAND(0.02) * speed);
249 s->spinner_speed = (BELLRAND(0.3) * speed);
251 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
254 s->wave_position = 0;
255 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
258 for (i = 0; i < GRID_SIZE; i++)
261 (i < GRID_SIZE-5) && /* display approx. once per 5 strips */
262 !(random() % (GRID_SIZE-5)*5))
266 time_t now = time ((time_t *) 0);
267 struct tm *tm = localtime (&now);
268 strftime (text, sizeof(text)-1, timefmt, tm);
270 /* render time into the strip */
271 for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
273 s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
274 s->highlight[i] = True;
277 time_displayed_p = True;
281 int draw_p = (random() % 7);
282 int spin_p = (draw_p && !(random() % 20));
284 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
288 s->highlight[i] = False;
291 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
295 /* Animate the strip one step. Reset if it has reached the bottom.
298 tick_strip (ModeInfo *mi, strip *s)
300 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
303 if (mp->button_down_p)
310 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
316 s->spinner_y += s->spinner_speed;
317 if (s->spinner_y >= GRID_SIZE)
328 s->spinner_speed /= 2; /* erase it slower than we drew it */
332 /* Spin the spinners. */
334 if (s->spin_tick > s->spin_speed)
337 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
338 for (i = 0; i < GRID_SIZE; i++)
339 if (s->glyphs[i] < 0)
341 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
342 if (! (random() % 800)) /* sometimes they stop spinning */
343 s->glyphs[i] = -s->glyphs[i];
347 /* Move the color (brightness) wave. */
349 if (s->wave_tick > s->wave_speed)
353 if (s->wave_position >= WAVE_SIZE)
354 s->wave_position = 0;
359 /* Draw a single character at the given position and brightness.
362 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
363 GLfloat x, GLfloat y, GLfloat z,
366 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
367 int wire = MI_IS_WIREFRAME(mi);
368 GLfloat w = mp->tex_char_width;
369 GLfloat h = mp->tex_char_height;
370 GLfloat cx = 0, cy = 0;
372 Bool spinner_p = (glyph < 0);
374 if (glyph == 0) abort();
375 if (glyph < 0) glyph = -glyph;
388 int ccx = ((glyph - 1) % CHAR_COLS);
389 int ccy = ((glyph - 1) / CHAR_COLS);
392 cy = (mp->real_char_rows - ccy - 1) * h;
397 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
398 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
399 brightness *= depth; /* so no row goes all black. */
409 if (!do_texture && !spinner_p)
416 /* If the glyph is very close to the screen (meaning it is very large,
417 and is about to splash into the screen and vanish) then start fading
418 it out, proportional to how close to the glass it is.
420 if (z > GRID_DEPTH/2)
422 GLfloat ratio = ((z - GRID_DEPTH/2) /
423 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
424 int i = ratio * WAVE_SIZE;
427 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
429 a *= mp->brightness_ramp[i];
435 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
436 glNormal3f (0, 0, 1);
437 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
438 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
439 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
440 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
443 if (wire && spinner_p)
446 glVertex3f (x, y, z);
447 glVertex3f (x+S, y+S, z);
448 glVertex3f (x, y+S, z);
449 glVertex3f (x+S, y, z);
457 /* Draw all the visible glyphs in the strip.
460 draw_strip (ModeInfo *mi, strip *s)
462 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
464 for (i = 0; i < GRID_SIZE; i++)
466 int g = s->glyphs[i];
467 Bool below_p = (s->spinner_y >= i);
472 if (g && below_p) /* don't draw cells below the spinner */
479 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
481 brightness = mp->brightness_ramp[j];
484 draw_glyph (mi, g, s->highlight[i],
485 s->x, s->y - i, s->z, brightness);
490 draw_glyph (mi, s->spinner_glyph, False,
491 s->x, s->y - s->spinner_y, s->z, 1.0);
495 /* qsort comparator for sorting strips by z position */
497 cmp_strips (const void *aa, const void *bb)
499 const strip *a = *(strip **) aa;
500 const strip *b = *(strip **) bb;
501 return ((int) (a->z * 10000) -
502 (int) (b->z * 10000));
510 auto_track_init (ModeInfo *mi)
512 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
515 mp->view_x = nice_views[mp->last_view].x;
516 mp->view_y = nice_views[mp->last_view].y;
517 mp->view_steps = 100;
519 mp->auto_tracking_p = False;
524 auto_track (ModeInfo *mi)
526 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
530 if (mp->button_down_p)
533 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
534 if (! mp->auto_tracking_p)
536 if (++mp->track_tick < 20/speed) return;
538 if (! (random() % 20))
539 mp->auto_tracking_p = True;
546 GLfloat ox = nice_views[mp->last_view].x;
547 GLfloat oy = nice_views[mp->last_view].y;
548 GLfloat tx = nice_views[mp->target_view].x;
549 GLfloat ty = nice_views[mp->target_view].y;
551 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
553 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
555 mp->view_x = (ox + ((tx - ox) * th));
556 mp->view_y = (oy + ((ty - oy) * th));
559 if (mp->view_tick >= mp->view_steps)
562 mp->view_steps = (350.0 / speed);
563 mp->last_view = mp->target_view;
564 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
565 mp->auto_tracking_p = False;
571 /* Window management, etc
574 reshape_matrix (ModeInfo *mi, int width, int height)
576 GLfloat h = (GLfloat) height / (GLfloat) width;
578 glViewport (0, 0, (GLint) width, (GLint) height);
580 glMatrixMode(GL_PROJECTION);
582 gluPerspective (80.0, 1/h, 1.0, 100);
584 glMatrixMode(GL_MODELVIEW);
586 gluLookAt( 0.0, 0.0, 25.0,
590 glClear(GL_COLOR_BUFFER_BIT);
595 matrix_handle_event (ModeInfo *mi, XEvent *event)
597 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
599 if (event->xany.type == ButtonPress &&
600 event->xbutton.button == Button1)
602 mp->button_down_p = True;
605 else if (event->xany.type == ButtonRelease &&
606 event->xbutton.button == Button1)
608 mp->button_down_p = False;
620 union { int i; char c[sizeof(int)]; } u;
627 /* The image with the characters in it is 512x598, meaning that it needs to
628 be copied into a 512x1024 texture. But some machines can't handle textures
629 that large... And it turns out that we aren't using most of the characters
630 in that image anyway, since this program doesn't do anything that makes use
631 of the full range of Latin1 characters. So... this function tosses out the
632 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
633 can then stuff in a 512x512 texture. Voila.
635 If this hack ever grows into something that displays full Latin1 text,
636 well then, Something Else Will Need To Be Done.
639 spank_image (matrix_configuration *mp, XImage *xi)
641 int ch = xi->height / CHAR_ROWS;
643 unsigned char *bits = (unsigned char *) xi->data;
644 unsigned char *from, *to, *s, *end;
645 int L = xi->bytes_per_line * ch;
648 /* Copy row 12 into 10 (which really means, copy 2 into 0,
649 since texture data is upside down.).
651 to = bits + (L * cut);
658 /* Then, pull all the bits down by 2 rows.
661 from = bits + (L * cut);
662 end = bits + (L * CHAR_ROWS);
667 /* And clear out the rest, for good measure.
669 from = bits + (L * (CHAR_ROWS - cut));
670 end = bits + (L * CHAR_ROWS);
675 xi->height -= (cut * ch);
676 mp->real_char_rows -= cut;
679 /* Finally, pull the map indexes back to match the new bits.
681 for (i = 0; i < countof(matrix_encoding); i++)
682 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
683 matrix_encoding[i] -= (cut * CHAR_COLS);
689 load_textures (ModeInfo *mi, Bool flip_p)
691 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
697 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
698 So we waste some padding rows to round up.
700 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
704 mp->real_char_rows = CHAR_ROWS;
705 spank_image (mp, xi);
707 if (xi->height != 512 && xi->height != 1024)
709 xi->height = (xi->height < 512 ? 512 : 1024);
710 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
713 fprintf(stderr, "%s: out of memory\n", progname);
718 if (xi->width != 512) abort();
719 if (xi->height != 512 && xi->height != 1024) abort();
721 /* char size in pixels */
722 cw = orig_w / CHAR_COLS;
723 ch = orig_h / CHAR_ROWS;
725 /* char size in ratio of final (padded) texture size */
726 mp->tex_char_width = (GLfloat) cw / xi->width;
727 mp->tex_char_height = (GLfloat) ch / xi->height;
729 /* Flip each character's bits horizontally -- we could also just do this
730 by reversing the texture coordinates on the quads, but on some systems
731 that slows things down a lot.
736 unsigned long buf[100];
737 for (y = 0; y < xi->height; y++)
738 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
740 for (x = 0; x < cw; x++)
741 buf[x] = XGetPixel (xi, xx+x, y);
742 for (x = 0; x < cw; x++)
743 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
747 /* The pixmap is a color image with no transparency. Set the texture's
748 alpha to be the green channel, and set the green channel to be 100%.
751 int rpos, gpos, bpos, apos; /* bitfield positions */
753 /* #### Cherub says that the little-endian case must be taken on MacOSX,
754 or else the colors/alpha are the wrong way around. How can
758 rpos = 24, gpos = 16, bpos = 8, apos = 0;
761 rpos = 0, gpos = 8, bpos = 16, apos = 24;
763 for (y = 0; y < xi->height; y++)
764 for (x = 0; x < xi->width; x++)
766 unsigned long p = XGetPixel (xi, x, y);
767 unsigned char r = (p >> rpos) & 0xFF;
768 unsigned char g = (p >> gpos) & 0xFF;
769 unsigned char b = (p >> bpos) & 0xFF;
772 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
773 XPutPixel (xi, x, y, p);
777 /* Now load the texture into GL.
780 glGenTextures (1, &mp->texture);
782 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
783 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
784 glBindTexture (GL_TEXTURE_2D, mp->texture);
785 check_gl_error ("texture init");
786 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
787 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
790 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
791 check_gl_error (buf);
794 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
795 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
797 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
798 faint solid green border around the texture if it is *not* REPEAT!
800 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
801 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
803 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
804 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
805 check_gl_error ("texture param");
812 init_matrix (ModeInfo *mi)
814 matrix_configuration *mp;
815 int wire = MI_IS_WIREFRAME(mi);
823 mps = (matrix_configuration *)
824 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
826 fprintf(stderr, "%s: out of memory\n", progname);
831 mp = &mps[MI_SCREEN(mi)];
832 mp->glx_context = init_GL(mi);
834 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
837 mp->glyph_map = matrix_encoding;
838 mp->nglyphs = countof(matrix_encoding);
840 else if (!strcasecmp (mode_str, "dna"))
843 mp->glyph_map = dna_encoding;
844 mp->nglyphs = countof(dna_encoding);
846 else if (!strcasecmp (mode_str, "bin") ||
847 !strcasecmp (mode_str, "binary"))
850 mp->glyph_map = binary_encoding;
851 mp->nglyphs = countof(binary_encoding);
853 else if (!strcasecmp (mode_str, "hex") ||
854 !strcasecmp (mode_str, "hexadecimal"))
857 mp->glyph_map = hex_encoding;
858 mp->nglyphs = countof(hex_encoding);
860 else if (!strcasecmp (mode_str, "dec") ||
861 !strcasecmp (mode_str, "decimal"))
864 mp->glyph_map = decimal_encoding;
865 mp->nglyphs = countof(decimal_encoding);
870 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
875 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
877 glShadeModel(GL_SMOOTH);
879 glDisable(GL_DEPTH_TEST);
880 glDisable(GL_CULL_FACE);
881 glEnable(GL_NORMALIZE);
885 load_textures (mi, flip_p);
886 glEnable(GL_TEXTURE_2D);
889 /* Jeff Epler points out:
890 By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
891 added to each other, so that a bright glyph with a darker one
892 in front is a little brighter than the bright glyph alone.
894 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
897 /* to scale coverage-percent to strips, this number looks about right... */
898 mp->nstrips = (int) (density * 2.2);
899 if (mp->nstrips < 1) mp->nstrips = 1;
900 else if (mp->nstrips > 2000) mp->nstrips = 2000;
903 mp->strips = calloc (mp->nstrips, sizeof(strip));
904 for (i = 0; i < mp->nstrips; i++)
906 strip *s = &mp->strips[i];
909 /* If we start all strips from zero at once, then the first few seconds
910 of the animation are much denser than normal. So instead, set all
911 the initial strips to erase-mode with random starting positions.
912 As these die off at random speeds and are re-created, we'll get a
913 more consistent density. */
915 s->spinner_y = frand(GRID_SIZE);
916 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
919 /* Compute the brightness ramp.
921 for (i = 0; i < WAVE_SIZE; i++)
923 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
924 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
925 j = sin (j); /* j ranges from 0.0 - 1.0 */
926 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
927 mp->brightness_ramp[i] = j;
928 /* printf("%2d %8.2f\n", i, j); */
932 auto_track_init (mi);
939 draw_grid (ModeInfo *mi)
941 if (!MI_IS_WIREFRAME(mi))
943 glDisable(GL_TEXTURE_2D);
950 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
951 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
953 glBegin(GL_LINE_LOOP);
954 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
955 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
956 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
957 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
959 glBegin(GL_LINE_LOOP);
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);
965 glBegin(GL_LINE_LOOP);
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);
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);
978 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
979 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
982 if (!MI_IS_WIREFRAME(mi))
984 glEnable(GL_TEXTURE_2D);
992 draw_matrix (ModeInfo *mi)
994 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
995 Display *dpy = MI_DISPLAY(mi);
996 Window window = MI_WINDOW(mi);
999 if (!mp->glx_context)
1002 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
1004 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1010 glRotatef (mp->view_x, 1, 0, 0);
1011 glRotatef (mp->view_y, 0, 1, 0);
1016 glScalef(0.5, 0.5, 0.5);
1019 glRotatef(-30, 0, 1, 0);
1024 mi->polygon_count = 0;
1026 /* Render (and tick) each strip, starting at the back
1027 (draw the ones farthest from the camera first, to make
1028 the alpha transparency work out right.)
1031 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1032 for (i = 0; i < mp->nstrips; i++)
1033 sorted[i] = &mp->strips[i];
1034 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1036 for (i = 0; i < mp->nstrips; i++)
1038 strip *s = sorted[i];
1050 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1051 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1052 glTexCoord2f (1,1); glVertex3f(15,15,0);
1053 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1059 if (mi->fps_p) do_fps (mi);
1062 glXSwapBuffers(dpy, window);
1065 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)