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 #include <X11/Intrinsic.h>
21 extern XtAppContext app;
23 #define PROGCLASS "GLMatrix"
24 #define HACK_INIT init_matrix
25 #define HACK_DRAW draw_matrix
26 #define HACK_RESHAPE reshape_matrix
27 #define HACK_HANDLE_EVENT matrix_handle_event
28 #define EVENT_MASK PointerMotionMask
29 #define matrix_opts xlockmore_opts
31 #define DEF_SPEED "1.0"
32 #define DEF_DENSITY "20"
33 #define DEF_CLOCK "False"
34 #define DEF_FOG "True"
35 #define DEF_WAVES "True"
36 #define DEF_ROTATE "True"
37 #define DEF_TEXTURE "True"
38 #define DEF_MODE "Matrix"
39 #define DEF_TIMEFMT " %l%M%p "
41 #define DEFAULTS "*delay: 30000 \n" \
42 "*showFPS: False \n" \
43 "*wireframe: False \n" \
46 #define countof(x) (sizeof((x))/sizeof((*x)))
49 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
51 #include "xlockmore.h"
52 #include "xpm-ximage.h"
58 __extension__ /* don't warn about "string length is greater than the length
59 ISO C89 compilers are required to support" when including
60 the following XPM file... */
62 #include "../images/matrix3.xpm"
64 #ifdef USE_GL /* whole file */
74 static int real_char_rows;
76 static int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
77 192, 193, 194, 195, 196, 197, 198, 199,
78 200, 201, 202, 203, 204, 205, 206, 207 };
79 static int decimal_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
80 static int hex_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
81 33, 34, 35, 36, 37, 38 };
82 static int binary_encoding[] = { 16, 17 };
83 static int dna_encoding[] = { 33, 35, 39, 52 };
85 static unsigned char char_map[256] = {
86 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */
87 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */
88 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
89 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
90 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
91 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
92 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
93 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
94 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */
95 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */
96 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
97 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
98 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
99 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
101 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
102 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
103 #else /* see spank_image() */
104 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */
105 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */
109 #define CURSOR_GLYPH 97
113 #define GRID_SIZE 70 /* width and height of the arena */
114 #define GRID_DEPTH 35 /* depth of the arena */
115 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
116 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
118 static struct { GLfloat x, y; } nice_views[] = {
120 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
121 { 0, 20 }, /* every now and then we switch to a new one. */
122 { 25, 0 }, /* (but we only use the first one at start-up.) */
131 { 0, 0 }, /* prefer these */
140 GLfloat x, y, z; /* position of strip */
141 GLfloat dx, dy, dz; /* velocity of strip */
143 Bool erasing_p; /* Whether this strip is on its way out. */
145 int spinner_glyph; /* the bottommost glyph -- the feeder */
146 GLfloat spinner_y; /* where on the strip the bottom glyph is */
147 GLfloat spinner_speed; /* how fast the bottom glyph drops */
149 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
150 revealed by the dropping spinner.
151 0 means no glyph; negative means "spinner".
152 If non-zero, real value is abs(G)-1. */
154 Bool highlight[GRID_SIZE];
155 /* some glyphs may be highlighted */
157 int spin_speed; /* Rotate all spinners every this-many frames */
158 int spin_tick; /* frame counter */
160 int wave_position; /* Waves of brightness wash down the strip. */
161 int wave_speed; /* every this-many frames. */
162 int wave_tick; /* frame counter. */
168 GLXContext *glx_context;
175 GLfloat tex_char_width, tex_char_height;
177 /* auto-tracking direction of view */
178 int last_view, target_view;
179 GLfloat view_x, view_y;
180 int view_steps, view_tick;
181 Bool auto_tracking_p;
183 } matrix_configuration;
185 static matrix_configuration *mps = NULL;
187 static GLfloat brightness_ramp[WAVE_SIZE];
189 static GLfloat speed;
190 static GLfloat density;
191 static Bool do_clock;
192 static char *timefmt;
194 static Bool do_waves;
195 static Bool do_rotate;
196 static Bool do_texture;
197 static char *mode_str;
199 static XrmOptionDescRec opts[] = {
200 { "-speed", ".speed", XrmoptionSepArg, 0 },
201 { "-density", ".density", XrmoptionSepArg, 0 },
202 { "-mode", ".mode", XrmoptionSepArg, 0 },
203 { "-binary", ".mode", XrmoptionNoArg, "binary" },
204 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
205 { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
206 { "-dna", ".mode", XrmoptionNoArg, "dna" },
207 { "-clock", ".clock", XrmoptionNoArg, "True" },
208 { "+clock", ".clock", XrmoptionNoArg, "False" },
209 { "-timefmt", ".timefmt", XrmoptionSepArg, 0 },
210 { "-fog", ".fog", XrmoptionNoArg, "True" },
211 { "+fog", ".fog", XrmoptionNoArg, "False" },
212 { "-waves", ".waves", XrmoptionNoArg, "True" },
213 { "+waves", ".waves", XrmoptionNoArg, "False" },
214 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
215 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
216 {"-texture", ".texture", XrmoptionNoArg, "True" },
217 {"+texture", ".texture", XrmoptionNoArg, "False" },
220 static argtype vars[] = {
221 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
222 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
223 {&density, "density", "Density", DEF_DENSITY, t_Float},
224 {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool},
225 {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String},
226 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
227 {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
228 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
229 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
232 ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
235 /* Re-randomize the state of one strip.
238 reset_strip (ModeInfo *mi, strip *s)
240 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
242 Bool time_displayed_p = False; /* never display time twice in one strip */
244 memset (s, 0, sizeof(*s));
245 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
246 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
247 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
251 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
253 s->dz = (BELLRAND(0.02) * speed);
255 s->spinner_speed = (BELLRAND(0.3) * speed);
257 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
260 s->wave_position = 0;
261 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
264 for (i = 0; i < GRID_SIZE; i++)
267 (i < GRID_SIZE-5) && /* display approx. once per 5 strips */
268 !(random() % (GRID_SIZE-5)*5))
272 time_t now = time ((time_t *) 0);
273 struct tm *tm = localtime (&now);
274 strftime (text, sizeof(text)-1, timefmt, tm);
276 /* render time into the strip */
277 for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
279 s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
280 s->highlight[i] = True;
283 time_displayed_p = True;
287 int draw_p = (random() % 7);
288 int spin_p = (draw_p && !(random() % 20));
290 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
294 s->highlight[i] = False;
297 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
301 /* Animate the strip one step. Reset if it has reached the bottom.
304 tick_strip (ModeInfo *mi, strip *s)
306 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
309 if (mp->button_down_p)
316 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
322 s->spinner_y += s->spinner_speed;
323 if (s->spinner_y >= GRID_SIZE)
334 s->spinner_speed /= 2; /* erase it slower than we drew it */
338 /* Spin the spinners. */
340 if (s->spin_tick > s->spin_speed)
343 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
344 for (i = 0; i < GRID_SIZE; i++)
345 if (s->glyphs[i] < 0)
347 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
348 if (! (random() % 800)) /* sometimes they stop spinning */
349 s->glyphs[i] = -s->glyphs[i];
353 /* Move the color (brightness) wave. */
355 if (s->wave_tick > s->wave_speed)
359 if (s->wave_position >= WAVE_SIZE)
360 s->wave_position = 0;
365 /* Draw a single character at the given position and brightness.
368 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
369 GLfloat x, GLfloat y, GLfloat z,
372 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
373 int wire = MI_IS_WIREFRAME(mi);
374 GLfloat w = mp->tex_char_width;
375 GLfloat h = mp->tex_char_height;
376 GLfloat cx = 0, cy = 0;
378 Bool spinner_p = (glyph < 0);
380 if (glyph == 0) abort();
381 if (glyph < 0) glyph = -glyph;
394 int ccx = ((glyph - 1) % CHAR_COLS);
395 int ccy = ((glyph - 1) / CHAR_COLS);
398 cy = (real_char_rows - ccy - 1) * h;
403 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
404 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
405 brightness *= depth; /* so no row goes all black. */
410 GLfloat r, g, b, a = 1;
415 if (!do_texture && !spinner_p)
416 r = b = 0, g = brightness;
418 r = g = b = brightness;
420 /* If the glyph is very close to the screen (meaning it is very large,
421 and is about to splash into the screen and vanish) then start fading
422 it out, proportional to how close to the glass it is.
424 if (z > GRID_DEPTH/2)
426 GLfloat ratio = ((z - GRID_DEPTH/2) /
427 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
428 int i = ratio * WAVE_SIZE;
431 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
433 a = brightness_ramp[i];
435 /* I don't understand this -- if I change the alpha on the color of
436 the quad, I'd expect that to make the quad more transparent.
437 But instead, it seems to be making the transparent parts of the
438 texture on the quad be *less* transparent! So as we fade out,
439 we fade towards a completely solid rectangle. WTF?
441 So, for now, instead of changing the alpha, just make the colors
442 be darker. This works out ok so long as we use GL_ONE in
443 glBlendFunc, so that stacked glyph colors are added together.
455 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
456 glNormal3f (0, 0, 1);
457 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
458 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
459 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
460 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
463 if (wire && spinner_p)
466 glVertex3f (x, y, z);
467 glVertex3f (x+S, y+S, z);
468 glVertex3f (x, y+S, z);
469 glVertex3f (x+S, y, z);
477 /* Draw all the visible glyphs in the strip.
480 draw_strip (ModeInfo *mi, strip *s)
483 for (i = 0; i < GRID_SIZE; i++)
485 int g = s->glyphs[i];
486 Bool below_p = (s->spinner_y >= i);
491 if (g && below_p) /* don't draw cells below the spinner */
498 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
500 brightness = brightness_ramp[j];
503 draw_glyph (mi, g, s->highlight[i],
504 s->x, s->y - i, s->z, brightness);
509 draw_glyph (mi, s->spinner_glyph, False,
510 s->x, s->y - s->spinner_y, s->z, 1.0);
514 /* qsort comparator for sorting strips by z position */
516 cmp_strips (const void *aa, const void *bb)
518 const strip *a = *(strip **) aa;
519 const strip *b = *(strip **) bb;
520 return ((int) (a->z * 10000) -
521 (int) (b->z * 10000));
529 auto_track_init (ModeInfo *mi)
531 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
534 mp->view_x = nice_views[mp->last_view].x;
535 mp->view_y = nice_views[mp->last_view].y;
536 mp->view_steps = 100;
538 mp->auto_tracking_p = False;
543 auto_track (ModeInfo *mi)
545 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
549 if (mp->button_down_p)
552 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
553 if (! mp->auto_tracking_p)
556 if (++tick < 20/speed) return;
558 if (! (random() % 20))
559 mp->auto_tracking_p = True;
566 GLfloat ox = nice_views[mp->last_view].x;
567 GLfloat oy = nice_views[mp->last_view].y;
568 GLfloat tx = nice_views[mp->target_view].x;
569 GLfloat ty = nice_views[mp->target_view].y;
571 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
573 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
575 mp->view_x = (ox + ((tx - ox) * th));
576 mp->view_y = (oy + ((ty - oy) * th));
579 if (mp->view_tick >= mp->view_steps)
582 mp->view_steps = (350.0 / speed);
583 mp->last_view = mp->target_view;
584 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
585 mp->auto_tracking_p = False;
591 /* Window management, etc
594 reshape_matrix (ModeInfo *mi, int width, int height)
596 GLfloat h = (GLfloat) height / (GLfloat) width;
598 glViewport (0, 0, (GLint) width, (GLint) height);
600 glMatrixMode(GL_PROJECTION);
602 gluPerspective (80.0, 1/h, 1.0, 100);
604 glMatrixMode(GL_MODELVIEW);
606 gluLookAt( 0.0, 0.0, 25.0,
610 glClear(GL_COLOR_BUFFER_BIT);
615 matrix_handle_event (ModeInfo *mi, XEvent *event)
617 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
619 if (event->xany.type == ButtonPress &&
620 event->xbutton.button == Button1)
622 mp->button_down_p = True;
625 else if (event->xany.type == ButtonRelease &&
626 event->xbutton.button == Button1)
628 mp->button_down_p = False;
640 union { int i; char c[sizeof(int)]; } u;
647 /* The image with the characters in it is 512x598, meaning that it needs to
648 be copied into a 512x1024 texture. But some machines can't handle textures
649 that large... And it turns out that we aren't using most of the characters
650 in that image anyway, since this program doesn't do anything that makes use
651 of the full range of Latin1 characters. So... this function tosses out the
652 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
653 can then stuff in a 512x512 texture. Voila.
655 If this hack ever grows into something that displays full Latin1 text,
656 well then, Something Else Will Need To Be Done.
659 spank_image (XImage *xi)
661 int ch = xi->height / CHAR_ROWS;
663 unsigned char *bits = (unsigned char *) xi->data;
664 unsigned char *from, *to, *s, *end;
665 int L = xi->bytes_per_line * ch;
668 /* Copy row 12 into 10 (which really means, copy 2 into 0,
669 since texture data is upside down.).
671 to = bits + (L * cut);
678 /* Then, pull all the bits down by 2 rows.
681 from = bits + (L * cut);
682 end = bits + (L * CHAR_ROWS);
687 /* And clear out the rest, for good measure.
689 from = bits + (L * (CHAR_ROWS - cut));
690 end = bits + (L * CHAR_ROWS);
695 xi->height -= (cut * ch);
696 real_char_rows -= cut;
698 /* Finally, pull the map indexes back to match the new bits.
700 for (i = 0; i < countof(matrix_encoding); i++)
701 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
702 matrix_encoding[i] -= (cut * CHAR_COLS);
707 load_textures (ModeInfo *mi, Bool flip_p)
709 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
715 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
716 So we waste some padding rows to round up.
718 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
722 real_char_rows = CHAR_ROWS;
725 if (xi->height != 512 && xi->height != 1024)
727 xi->height = (xi->height < 512 ? 512 : 1024);
728 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
731 fprintf(stderr, "%s: out of memory\n", progname);
736 if (xi->width != 512) abort();
737 if (xi->height != 512 && xi->height != 1024) abort();
739 /* char size in pixels */
740 cw = orig_w / CHAR_COLS;
741 ch = orig_h / CHAR_ROWS;
743 /* char size in ratio of final (padded) texture size */
744 mp->tex_char_width = (GLfloat) cw / xi->width;
745 mp->tex_char_height = (GLfloat) ch / xi->height;
747 /* Flip each character's bits horizontally -- we could also just do this
748 by reversing the texture coordinates on the quads, but on some systems
749 that slows things down a lot.
754 unsigned long buf[100];
755 for (y = 0; y < xi->height; y++)
756 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
758 for (x = 0; x < cw; x++)
759 buf[x] = XGetPixel (xi, xx+x, y);
760 for (x = 0; x < cw; x++)
761 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
765 /* The pixmap is a color image with no transparency. Set the texture's
766 alpha to be the green channel, and set the green channel to be 100%.
769 int rpos, gpos, bpos, apos; /* bitfield positions */
771 /* #### Cherub says that the little-endian case must be taken on MacOSX,
772 or else the colors/alpha are the wrong way around. How can
776 rpos = 24, gpos = 16, bpos = 8, apos = 0;
779 rpos = 0, gpos = 8, bpos = 16, apos = 24;
781 for (y = 0; y < xi->height; y++)
782 for (x = 0; x < xi->width; x++)
784 unsigned long p = XGetPixel (xi, x, y);
785 unsigned char r = (p >> rpos) & 0xFF;
786 unsigned char g = (p >> gpos) & 0xFF;
787 unsigned char b = (p >> bpos) & 0xFF;
788 unsigned char a = ~g;
790 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
791 XPutPixel (xi, x, y, p);
795 /* Now load the texture into GL.
798 glGenTextures (1, &mp->texture);
800 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
801 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
802 glBindTexture (GL_TEXTURE_2D, mp->texture);
803 check_gl_error ("texture init");
804 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
805 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
808 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
809 check_gl_error (buf);
812 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
813 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
815 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
816 faint solid green border around the texture if it is *not* REPEAT!
818 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
819 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
821 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
822 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
823 check_gl_error ("texture param");
830 init_matrix (ModeInfo *mi)
832 matrix_configuration *mp;
833 int wire = MI_IS_WIREFRAME(mi);
841 mps = (matrix_configuration *)
842 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
844 fprintf(stderr, "%s: out of memory\n", progname);
849 mp = &mps[MI_SCREEN(mi)];
850 mp->glx_context = init_GL(mi);
852 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
855 mp->glyph_map = matrix_encoding;
856 mp->nglyphs = countof(matrix_encoding);
858 else if (!strcasecmp (mode_str, "dna"))
861 mp->glyph_map = dna_encoding;
862 mp->nglyphs = countof(dna_encoding);
864 else if (!strcasecmp (mode_str, "bin") ||
865 !strcasecmp (mode_str, "binary"))
868 mp->glyph_map = binary_encoding;
869 mp->nglyphs = countof(binary_encoding);
871 else if (!strcasecmp (mode_str, "hex") ||
872 !strcasecmp (mode_str, "hexadecimal"))
875 mp->glyph_map = hex_encoding;
876 mp->nglyphs = countof(hex_encoding);
878 else if (!strcasecmp (mode_str, "dec") ||
879 !strcasecmp (mode_str, "decimal"))
882 mp->glyph_map = decimal_encoding;
883 mp->nglyphs = countof(decimal_encoding);
888 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
893 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
895 glShadeModel(GL_SMOOTH);
897 glDisable(GL_DEPTH_TEST);
898 glDisable(GL_CULL_FACE);
899 glEnable(GL_NORMALIZE);
903 load_textures (mi, flip_p);
904 glEnable(GL_TEXTURE_2D);
907 /* Jeff Epler points out:
908 By using GL_ONE instead of GL_SRC_ALPHA, glyphs are added to
909 each other, so that a bright glyph with a darker one in front
910 is a little brighter than the bright glyph alone.
912 glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, /* GL_SRC_ALPHA */ GL_ONE);
915 /* to scale coverage-percent to strips, this number looks about right... */
916 mp->nstrips = (int) (density * 2.2);
917 if (mp->nstrips < 1) mp->nstrips = 1;
918 else if (mp->nstrips > 2000) mp->nstrips = 2000;
921 mp->strips = calloc (mp->nstrips, sizeof(strip));
922 for (i = 0; i < mp->nstrips; i++)
924 strip *s = &mp->strips[i];
927 /* If we start all strips from zero at once, then the first few seconds
928 of the animation are much denser than normal. So instead, set all
929 the initial strips to erase-mode with random starting positions.
930 As these die off at random speeds and are re-created, we'll get a
931 more consistent density. */
933 s->spinner_y = frand(GRID_SIZE);
934 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
937 /* Compute the brightness ramp.
939 for (i = 0; i < WAVE_SIZE; i++)
941 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
942 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
943 j = sin (j); /* j ranges from 0.0 - 1.0 */
944 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
945 brightness_ramp[i] = j;
946 /* printf("%2d %8.2f\n", i, j); */
950 auto_track_init (mi);
957 draw_grid (ModeInfo *mi)
959 if (!MI_IS_WIREFRAME(mi))
961 glDisable(GL_TEXTURE_2D);
967 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
968 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
970 glBegin(GL_LINE_LOOP);
971 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
972 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
973 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
974 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
976 glBegin(GL_LINE_LOOP);
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);
980 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
982 glBegin(GL_LINE_LOOP);
983 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
984 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
985 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
986 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
989 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
990 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
991 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
992 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
993 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
994 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
995 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
996 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
999 if (!MI_IS_WIREFRAME(mi))
1001 glEnable(GL_TEXTURE_2D);
1009 draw_matrix (ModeInfo *mi)
1011 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
1012 Display *dpy = MI_DISPLAY(mi);
1013 Window window = MI_WINDOW(mi);
1016 if (!mp->glx_context)
1019 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1025 glRotatef (mp->view_x, 1, 0, 0);
1026 glRotatef (mp->view_y, 0, 1, 0);
1031 glScalef(0.5, 0.5, 0.5);
1034 glRotatef(-30, 0, 1, 0);
1039 mi->polygon_count = 0;
1041 /* Render (and tick) each strip, starting at the back
1042 (draw the ones farthest from the camera first, to make
1043 the alpha transparency work out right.)
1046 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1047 for (i = 0; i < mp->nstrips; i++)
1048 sorted[i] = &mp->strips[i];
1049 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1051 for (i = 0; i < mp->nstrips; i++)
1053 strip *s = sorted[i];
1065 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1066 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1067 glTexCoord2f (1,1); glVertex3f(15,15,0);
1068 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1074 if (mi->fps_p) do_fps (mi);
1077 glXSwapBuffers(dpy, window);