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_FOG "True"
34 #define DEF_WAVES "True"
35 #define DEF_ROTATE "True"
36 #define DEF_TEXTURE "True"
37 #define DEF_MODE "Matrix"
39 #define DEFAULTS "*delay: 30000 \n" \
40 "*showFPS: False \n" \
41 "*wireframe: False \n" \
42 "*mode: " DEF_MODE " \n" \
43 "*speed: " DEF_SPEED " \n" \
44 "*density: " DEF_DENSITY " \n" \
45 "*fog: " DEF_FOG " \n" \
46 "*waves: " DEF_WAVES " \n" \
47 "*texture: " DEF_TEXTURE " \n" \
48 "*rotate: " DEF_ROTATE " \n" \
51 #define countof(x) (sizeof((x))/sizeof((*x)))
54 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
56 #include "xlockmore.h"
57 #include "xpm-ximage.h"
61 __extension__ /* don't warn about "string length is greater than the length
62 ISO C89 compilers are required to support" when including
63 the following XPM file... */
65 #include "../images/matrix3.xpm"
67 #ifdef USE_GL /* whole file */
77 static int real_char_rows;
79 static int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
80 192, 193, 194, 195, 196, 197, 198, 199,
81 200, 201, 202, 203, 204, 205, 206, 207 };
82 static int decimal_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
83 static int hex_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
84 33, 34, 35, 36, 37, 38 };
85 static int binary_encoding[] = { 16, 17 };
86 static int dna_encoding[] = { 33, 35, 39, 52 };
88 static unsigned char char_map[256] = {
89 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0 */
90 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 16 */
91 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
92 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
93 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
94 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
95 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
96 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
97 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 128 */
98 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 144 */
99 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
100 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
101 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
102 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
103 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
104 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
108 #define CURSOR_GLYPH 97
112 #define GRID_SIZE 70 /* width and height of the arena */
113 #define GRID_DEPTH 35 /* depth of the arena */
114 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
115 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
117 static struct { GLfloat x, y; } nice_views[] = {
119 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
120 { 0, 20 }, /* every now and then we switch to a new one. */
121 { 25, 0 }, /* (but we only use the first one at start-up.) */
130 { 0, 0 }, /* prefer these */
139 GLfloat x, y, z; /* position of strip */
140 GLfloat dx, dy, dz; /* velocity of strip */
142 Bool erasing_p; /* Whether this strip is on its way out. */
144 int spinner_glyph; /* the bottommost glyph -- the feeder */
145 GLfloat spinner_y; /* where on the strip the bottom glyph is */
146 GLfloat spinner_speed; /* how fast the bottom glyph drops */
148 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
149 revealed by the dropping spinner.
150 0 means no glyph; negative means "spinner".
151 If non-zero, real value is abs(G)-1. */
153 int spin_speed; /* Rotate all spinners every this-many frames */
154 int spin_tick; /* frame counter */
156 int wave_position; /* Waves of brightness wash down the strip. */
157 int wave_speed; /* every this-many frames. */
158 int wave_tick; /* frame counter. */
164 GLXContext *glx_context;
171 GLfloat tex_char_width, tex_char_height;
173 /* auto-tracking direction of view */
174 int last_view, target_view;
175 GLfloat view_x, view_y;
176 int view_steps, view_tick;
177 Bool auto_tracking_p;
179 } matrix_configuration;
181 static matrix_configuration *mps = NULL;
183 static GLfloat brightness_ramp[WAVE_SIZE];
185 static GLfloat speed;
186 static GLfloat density;
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 { "-fog", ".fog", XrmoptionNoArg, "True" },
202 { "+fog", ".fog", XrmoptionNoArg, "False" },
203 { "-waves", ".waves", XrmoptionNoArg, "True" },
204 { "+waves", ".waves", XrmoptionNoArg, "False" },
205 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
206 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
207 {"-texture", ".texture", XrmoptionNoArg, "True" },
208 {"+texture", ".texture", XrmoptionNoArg, "False" },
211 static argtype vars[] = {
212 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
213 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
214 {&density, "density", "Density", DEF_DENSITY, t_Float},
215 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
216 {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
217 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
218 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
221 ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
224 /* Re-randomize the state of one strip.
227 reset_strip (ModeInfo *mi, strip *s)
229 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
232 memset (s, 0, sizeof(*s));
233 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
234 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
235 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
239 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
241 s->dz = (BELLRAND(0.02) * speed);
243 s->spinner_speed = (BELLRAND(0.3) * speed);
245 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
248 s->wave_position = 0;
249 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
252 for (i = 0; i < GRID_SIZE; i++)
254 int draw_p = (random() % 7);
255 int spin_p = (draw_p && !(random() % 20));
257 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
262 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
266 /* Animate the strip one step. Reset if it has reached the bottom.
269 tick_strip (ModeInfo *mi, strip *s)
271 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
274 if (mp->button_down_p)
281 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
287 s->spinner_y += s->spinner_speed;
288 if (s->spinner_y >= GRID_SIZE)
299 s->spinner_speed /= 2; /* erase it slower than we drew it */
303 /* Spin the spinners. */
305 if (s->spin_tick > s->spin_speed)
308 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
309 for (i = 0; i < GRID_SIZE; i++)
310 if (s->glyphs[i] < 0)
312 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
313 if (! (random() % 800)) /* sometimes they stop spinning */
314 s->glyphs[i] = -s->glyphs[i];
318 /* Move the color (brightness) wave. */
320 if (s->wave_tick > s->wave_speed)
324 if (s->wave_position >= WAVE_SIZE)
325 s->wave_position = 0;
330 /* Draw a single character at the given position and brightness.
333 draw_glyph (ModeInfo *mi, int glyph,
334 GLfloat x, GLfloat y, GLfloat z,
337 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
338 int wire = MI_IS_WIREFRAME(mi);
339 GLfloat w = mp->tex_char_width;
340 GLfloat h = mp->tex_char_height;
341 GLfloat cx = 0, cy = 0;
343 Bool spinner_p = (glyph < 0);
345 if (glyph == 0) abort();
346 if (glyph < 0) glyph = -glyph;
359 int ccx = ((glyph - 1) % CHAR_COLS);
360 int ccy = ((glyph - 1) / CHAR_COLS);
363 cy = (real_char_rows - ccy - 1) * h;
368 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
369 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
370 brightness *= depth; /* so no row goes all black. */
375 GLfloat r, g, b, a = 1;
376 if (!do_texture && !spinner_p)
377 r = b = 0, g = brightness;
379 r = g = b = brightness;
381 /* If the glyph is very close to the screen (meaning it is very large,
382 and is about to splash into the screen and vanish) then start fading
383 it out, proportional to how close to the glass it is.
385 if (z > GRID_DEPTH/2)
387 GLfloat ratio = ((z - GRID_DEPTH/2) /
388 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
389 int i = ratio * WAVE_SIZE;
392 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
394 a = brightness_ramp[i];
396 /* I don't understand this -- if I change the alpha on the color of
397 the quad, I'd expect that to make the quad more transparent.
398 But instead, it seems to be making the transparent parts of the
399 texture on the quad be *less* transparent! So as we fade out,
400 we fade towards a completely solid rectangle. WTF?
402 So, for now, instead of changing the alpha, just make the colors
403 be darker. This works out ok so long as we use GL_ONE in
404 glBlendFunc, so that stacked glyph colors are added together.
416 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
417 glNormal3f (0, 0, 1);
418 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
419 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
420 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
421 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
424 if (wire && spinner_p)
427 glVertex3f (x, y, z);
428 glVertex3f (x+S, y+S, z);
429 glVertex3f (x, y+S, z);
430 glVertex3f (x+S, y, z);
438 /* Draw all the visible glyphs in the strip.
441 draw_strip (ModeInfo *mi, strip *s)
444 for (i = 0; i < GRID_SIZE; i++)
446 int g = s->glyphs[i];
447 Bool below_p = (s->spinner_y >= i);
452 if (g && below_p) /* don't draw cells below the spinner */
459 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
461 brightness = brightness_ramp[j];
464 draw_glyph (mi, g, s->x, s->y - i, s->z, brightness);
469 draw_glyph (mi, s->spinner_glyph, s->x, s->y - s->spinner_y, s->z, 1.0);
473 /* qsort comparator for sorting strips by z position */
475 cmp_strips (const void *aa, const void *bb)
477 const strip *a = *(strip **) aa;
478 const strip *b = *(strip **) bb;
479 return ((int) (a->z * 10000) -
480 (int) (b->z * 10000));
488 auto_track_init (ModeInfo *mi)
490 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
493 mp->view_x = nice_views[mp->last_view].x;
494 mp->view_y = nice_views[mp->last_view].y;
495 mp->view_steps = 100;
497 mp->auto_tracking_p = False;
502 auto_track (ModeInfo *mi)
504 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
508 if (mp->button_down_p)
511 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
512 if (! mp->auto_tracking_p)
515 if (++tick < 20/speed) return;
517 if (! (random() % 20))
518 mp->auto_tracking_p = True;
525 GLfloat ox = nice_views[mp->last_view].x;
526 GLfloat oy = nice_views[mp->last_view].y;
527 GLfloat tx = nice_views[mp->target_view].x;
528 GLfloat ty = nice_views[mp->target_view].y;
530 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
532 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
534 mp->view_x = (ox + ((tx - ox) * th));
535 mp->view_y = (oy + ((ty - oy) * th));
538 if (mp->view_tick >= mp->view_steps)
541 mp->view_steps = (350.0 / speed);
542 mp->last_view = mp->target_view;
543 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
544 mp->auto_tracking_p = False;
550 /* Window management, etc
553 reshape_matrix (ModeInfo *mi, int width, int height)
555 GLfloat h = (GLfloat) height / (GLfloat) width;
557 glViewport (0, 0, (GLint) width, (GLint) height);
559 glMatrixMode(GL_PROJECTION);
561 gluPerspective (80.0, 1/h, 1.0, 100);
563 glMatrixMode(GL_MODELVIEW);
565 gluLookAt( 0.0, 0.0, 25.0,
569 glClear(GL_COLOR_BUFFER_BIT);
574 matrix_handle_event (ModeInfo *mi, XEvent *event)
576 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
578 if (event->xany.type == ButtonPress &&
579 event->xbutton.button & Button1)
581 mp->button_down_p = True;
584 else if (event->xany.type == ButtonRelease &&
585 event->xbutton.button & Button1)
587 mp->button_down_p = False;
599 union { int i; char c[sizeof(int)]; } u;
606 /* The image with the characters in it is 512x598, meaning that it needs to
607 be copied into a 512x1024 texture. But some machines can't handle textures
608 that large... And it turns out that we aren't using most of the characters
609 in that image anyway, since this program doesn't do anything that makes use
610 of the full range of Latin1 characters. So... this function tosses out the
611 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
612 can then stuff in a 512x512 texture. Voila.
614 If this hack ever grows into something that displays full Latin1 text,
615 well then, Something Else Will Need To Be Done.
618 spank_image (XImage *xi)
620 int ch = xi->height / CHAR_ROWS;
622 unsigned char *bits = (unsigned char *) xi->data;
623 unsigned char *from, *to, *s, *end;
624 int L = xi->bytes_per_line * ch;
627 /* Copy row 12 into 10 (which really means, copy 2 into 0,
628 since texture data is upside down.).
630 to = bits + (L * cut);
637 /* Then, pull all the bits down by 2 rows.
640 from = bits + (L * cut);
641 end = bits + (L * CHAR_ROWS);
646 /* And clear out the rest, for good measure.
648 from = bits + (L * (CHAR_ROWS - cut));
649 end = bits + (L * CHAR_ROWS);
654 xi->height -= (cut * ch);
655 real_char_rows -= cut;
657 /* Finally, pull the map indexes back to match the new bits.
659 for (i = 0; i < countof(matrix_encoding); i++)
660 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
661 matrix_encoding[i] -= (cut * CHAR_COLS);
666 load_textures (ModeInfo *mi, Bool flip_p)
668 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
674 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
675 So we waste some padding rows to round up.
677 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
681 real_char_rows = CHAR_ROWS;
684 if (xi->height != 512 && xi->height != 1024)
686 xi->height = (xi->height < 512 ? 512 : 1024);
687 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
690 fprintf(stderr, "%s: out of memory\n", progname);
695 if (xi->width != 512) abort();
696 if (xi->height != 512 && xi->height != 1024) abort();
698 /* char size in pixels */
699 cw = orig_w / CHAR_COLS;
700 ch = orig_h / CHAR_ROWS;
702 /* char size in ratio of final (padded) texture size */
703 mp->tex_char_width = (GLfloat) cw / xi->width;
704 mp->tex_char_height = (GLfloat) ch / xi->height;
706 /* Flip each character's bits horizontally -- we could also just do this
707 by reversing the texture coordinates on the quads, but on some systems
708 that slows things down a lot.
713 unsigned long buf[100];
714 for (y = 0; y < xi->height; y++)
715 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
717 for (x = 0; x < cw; x++)
718 buf[x] = XGetPixel (xi, xx+x, y);
719 for (x = 0; x < cw; x++)
720 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
724 /* The pixmap is a color image with no transparency. Set the texture's
725 alpha to be the green channel, and set the green channel to be 100%.
728 int rpos, gpos, bpos, apos; /* bitfield positions */
730 /* #### Cherub says that the little-endian case must be taken on MacOSX,
731 or else the colors/alpha are the wrong way around. How can
735 rpos = 24, gpos = 16, bpos = 8, apos = 0;
738 rpos = 0, gpos = 8, bpos = 16, apos = 24;
740 for (y = 0; y < xi->height; y++)
741 for (x = 0; x < xi->width; x++)
743 unsigned long p = XGetPixel (xi, x, y);
744 unsigned char r = (p >> rpos) & 0xFF;
745 unsigned char g = (p >> gpos) & 0xFF;
746 unsigned char b = (p >> bpos) & 0xFF;
747 unsigned char a = ~g;
749 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
750 XPutPixel (xi, x, y, p);
754 /* Now load the texture into GL.
757 glGenTextures (1, &mp->texture);
759 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
760 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
761 glBindTexture (GL_TEXTURE_2D, mp->texture);
762 check_gl_error ("texture init");
763 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
764 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
767 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
768 check_gl_error (buf);
771 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
772 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
774 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
775 faint solid green border around the texture if it is *not* REPEAT!
777 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
778 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
780 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
781 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
782 check_gl_error ("texture param");
784 xi->data = 0; /* don't free the texture data */
790 init_matrix (ModeInfo *mi)
792 matrix_configuration *mp;
793 int wire = MI_IS_WIREFRAME(mi);
801 mps = (matrix_configuration *)
802 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
804 fprintf(stderr, "%s: out of memory\n", progname);
809 mp = &mps[MI_SCREEN(mi)];
810 mp->glx_context = init_GL(mi);
812 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
815 mp->glyph_map = matrix_encoding;
816 mp->nglyphs = countof(matrix_encoding);
818 else if (!strcasecmp (mode_str, "dna"))
821 mp->glyph_map = dna_encoding;
822 mp->nglyphs = countof(dna_encoding);
824 else if (!strcasecmp (mode_str, "bin") ||
825 !strcasecmp (mode_str, "binary"))
828 mp->glyph_map = binary_encoding;
829 mp->nglyphs = countof(binary_encoding);
831 else if (!strcasecmp (mode_str, "hex") ||
832 !strcasecmp (mode_str, "hexadecimal"))
835 mp->glyph_map = hex_encoding;
836 mp->nglyphs = countof(hex_encoding);
838 else if (!strcasecmp (mode_str, "dec") ||
839 !strcasecmp (mode_str, "decimal"))
842 mp->glyph_map = decimal_encoding;
843 mp->nglyphs = countof(decimal_encoding);
848 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
853 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
855 glShadeModel(GL_SMOOTH);
857 glDisable(GL_DEPTH_TEST);
858 glDisable(GL_CULL_FACE);
859 glEnable(GL_NORMALIZE);
863 load_textures (mi, flip_p);
864 glEnable(GL_TEXTURE_2D);
867 /* Jeff Epler points out:
868 By using GL_ONE instead of GL_SRC_ALPHA, glyphs are added to
869 each other, so that a bright glyph with a darker one in front
870 is a little brighter than the bright glyph alone.
872 glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, /* GL_SRC_ALPHA */ GL_ONE);
875 /* to scale coverage-percent to strips, this number looks about right... */
876 mp->nstrips = (int) (density * 2.2);
877 if (mp->nstrips < 1) mp->nstrips = 1;
878 else if (mp->nstrips > 2000) mp->nstrips = 2000;
881 mp->strips = calloc (mp->nstrips, sizeof(strip));
882 for (i = 0; i < mp->nstrips; i++)
884 strip *s = &mp->strips[i];
887 /* If we start all strips from zero at once, then the first few seconds
888 of the animation are much denser than normal. So instead, set all
889 the initial strips to erase-mode with random starting positions.
890 As these die off at random speeds and are re-created, we'll get a
891 more consistent density. */
893 s->spinner_y = frand(GRID_SIZE);
894 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
897 /* Compute the brightness ramp.
899 for (i = 0; i < WAVE_SIZE; i++)
901 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
902 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
903 j = sin (j); /* j ranges from 0.0 - 1.0 */
904 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
905 brightness_ramp[i] = j;
906 /* printf("%2d %8.2f\n", i, j); */
910 auto_track_init (mi);
917 draw_grid (ModeInfo *mi)
919 if (!MI_IS_WIREFRAME(mi))
921 glDisable(GL_TEXTURE_2D);
927 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
928 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
930 glBegin(GL_LINE_LOOP);
931 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
932 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
933 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
934 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
936 glBegin(GL_LINE_LOOP);
937 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
938 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
939 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
940 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
942 glBegin(GL_LINE_LOOP);
943 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
944 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
945 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
946 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
949 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
950 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
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);
955 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
956 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
959 if (!MI_IS_WIREFRAME(mi))
961 glEnable(GL_TEXTURE_2D);
969 draw_matrix (ModeInfo *mi)
971 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
972 Display *dpy = MI_DISPLAY(mi);
973 Window window = MI_WINDOW(mi);
976 if (!mp->glx_context)
979 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
985 glRotatef (mp->view_x, 1, 0, 0);
986 glRotatef (mp->view_y, 0, 1, 0);
991 glScalef(0.5, 0.5, 0.5);
994 glRotatef(-30, 0, 1, 0);
999 mi->polygon_count = 0;
1001 /* Render (and tick) each strip, starting at the back
1002 (draw the ones farthest from the camera first, to make
1003 the alpha transparency work out right.)
1006 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1007 for (i = 0; i < mp->nstrips; i++)
1008 sorted[i] = &mp->strips[i];
1009 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1011 for (i = 0; i < mp->nstrips; i++)
1013 strip *s = sorted[i];
1025 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1026 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1027 glTexCoord2f (1,1); glVertex3f(15,15,0);
1028 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1034 if (mi->fps_p) do_fps (mi);
1037 glXSwapBuffers(dpy, window);