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" \
44 "*mode: " DEF_MODE " \n" \
45 "*speed: " DEF_SPEED " \n" \
46 "*clock: " DEF_CLOCK " \n" \
47 "*timefmt: " DEF_TIMEFMT " \n" \
48 "*density: " DEF_DENSITY " \n" \
49 "*fog: " DEF_FOG " \n" \
50 "*waves: " DEF_WAVES " \n" \
51 "*texture: " DEF_TEXTURE " \n" \
52 "*rotate: " DEF_ROTATE " \n" \
55 #define countof(x) (sizeof((x))/sizeof((*x)))
58 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
60 #include "xlockmore.h"
61 #include "xpm-ximage.h"
67 __extension__ /* don't warn about "string length is greater than the length
68 ISO C89 compilers are required to support" when including
69 the following XPM file... */
71 #include "../images/matrix3.xpm"
73 #ifdef USE_GL /* whole file */
83 static int real_char_rows;
85 static int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
86 192, 193, 194, 195, 196, 197, 198, 199,
87 200, 201, 202, 203, 204, 205, 206, 207 };
88 static int decimal_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
89 static int hex_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
90 33, 34, 35, 36, 37, 38 };
91 static int binary_encoding[] = { 16, 17 };
92 static int dna_encoding[] = { 33, 35, 39, 52 };
94 static unsigned char char_map[256] = {
95 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */
96 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */
97 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
98 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
99 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
100 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
101 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
102 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
103 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */
104 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */
105 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
106 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
107 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
108 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
110 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
111 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
112 #else /* see spank_image() */
113 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */
114 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */
118 #define CURSOR_GLYPH 97
122 #define GRID_SIZE 70 /* width and height of the arena */
123 #define GRID_DEPTH 35 /* depth of the arena */
124 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
125 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
127 static struct { GLfloat x, y; } nice_views[] = {
129 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
130 { 0, 20 }, /* every now and then we switch to a new one. */
131 { 25, 0 }, /* (but we only use the first one at start-up.) */
140 { 0, 0 }, /* prefer these */
149 GLfloat x, y, z; /* position of strip */
150 GLfloat dx, dy, dz; /* velocity of strip */
152 Bool erasing_p; /* Whether this strip is on its way out. */
154 int spinner_glyph; /* the bottommost glyph -- the feeder */
155 GLfloat spinner_y; /* where on the strip the bottom glyph is */
156 GLfloat spinner_speed; /* how fast the bottom glyph drops */
158 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
159 revealed by the dropping spinner.
160 0 means no glyph; negative means "spinner".
161 If non-zero, real value is abs(G)-1. */
163 Bool highlight[GRID_SIZE];
164 /* some glyphs may be highlighted */
166 int spin_speed; /* Rotate all spinners every this-many frames */
167 int spin_tick; /* frame counter */
169 int wave_position; /* Waves of brightness wash down the strip. */
170 int wave_speed; /* every this-many frames. */
171 int wave_tick; /* frame counter. */
177 GLXContext *glx_context;
184 GLfloat tex_char_width, tex_char_height;
186 /* auto-tracking direction of view */
187 int last_view, target_view;
188 GLfloat view_x, view_y;
189 int view_steps, view_tick;
190 Bool auto_tracking_p;
192 } matrix_configuration;
194 static matrix_configuration *mps = NULL;
196 static GLfloat brightness_ramp[WAVE_SIZE];
198 static GLfloat speed;
199 static GLfloat density;
200 static Bool do_clock;
201 static char *timefmt;
203 static Bool do_waves;
204 static Bool do_rotate;
205 static Bool do_texture;
206 static char *mode_str;
208 static XrmOptionDescRec opts[] = {
209 { "-speed", ".speed", XrmoptionSepArg, 0 },
210 { "-density", ".density", XrmoptionSepArg, 0 },
211 { "-mode", ".mode", XrmoptionSepArg, 0 },
212 { "-binary", ".mode", XrmoptionNoArg, "binary" },
213 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
214 { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
215 { "-dna", ".mode", XrmoptionNoArg, "dna" },
216 { "-clock", ".clock", XrmoptionNoArg, "True" },
217 { "+clock", ".clock", XrmoptionNoArg, "False" },
218 { "-timefmt", ".timefmt", XrmoptionSepArg, 0 },
219 { "-fog", ".fog", XrmoptionNoArg, "True" },
220 { "+fog", ".fog", XrmoptionNoArg, "False" },
221 { "-waves", ".waves", XrmoptionNoArg, "True" },
222 { "+waves", ".waves", XrmoptionNoArg, "False" },
223 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
224 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
225 {"-texture", ".texture", XrmoptionNoArg, "True" },
226 {"+texture", ".texture", XrmoptionNoArg, "False" },
229 static argtype vars[] = {
230 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
231 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
232 {&density, "density", "Density", DEF_DENSITY, t_Float},
233 {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool},
234 {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String},
235 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
236 {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
237 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
238 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
241 ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
244 /* Re-randomize the state of one strip.
247 reset_strip (ModeInfo *mi, strip *s)
249 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
251 Bool time_displayed_p = False; /* never display time twice in one strip */
253 memset (s, 0, sizeof(*s));
254 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
255 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
256 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
260 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
262 s->dz = (BELLRAND(0.02) * speed);
264 s->spinner_speed = (BELLRAND(0.3) * speed);
266 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
269 s->wave_position = 0;
270 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
273 for (i = 0; i < GRID_SIZE; i++)
276 (i < GRID_SIZE-5) && /* display approx. once per 5 strips */
277 !(random() % (GRID_SIZE-5)*5))
281 time_t now = time ((time_t *) 0);
282 struct tm *tm = localtime (&now);
283 strftime (text, sizeof(text)-1, timefmt, tm);
285 /* render time into the strip */
286 for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
288 s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
289 s->highlight[i] = True;
292 time_displayed_p = True;
296 int draw_p = (random() % 7);
297 int spin_p = (draw_p && !(random() % 20));
299 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
303 s->highlight[i] = False;
306 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
310 /* Animate the strip one step. Reset if it has reached the bottom.
313 tick_strip (ModeInfo *mi, strip *s)
315 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
318 if (mp->button_down_p)
325 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
331 s->spinner_y += s->spinner_speed;
332 if (s->spinner_y >= GRID_SIZE)
343 s->spinner_speed /= 2; /* erase it slower than we drew it */
347 /* Spin the spinners. */
349 if (s->spin_tick > s->spin_speed)
352 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
353 for (i = 0; i < GRID_SIZE; i++)
354 if (s->glyphs[i] < 0)
356 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
357 if (! (random() % 800)) /* sometimes they stop spinning */
358 s->glyphs[i] = -s->glyphs[i];
362 /* Move the color (brightness) wave. */
364 if (s->wave_tick > s->wave_speed)
368 if (s->wave_position >= WAVE_SIZE)
369 s->wave_position = 0;
374 /* Draw a single character at the given position and brightness.
377 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
378 GLfloat x, GLfloat y, GLfloat z,
381 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
382 int wire = MI_IS_WIREFRAME(mi);
383 GLfloat w = mp->tex_char_width;
384 GLfloat h = mp->tex_char_height;
385 GLfloat cx = 0, cy = 0;
387 Bool spinner_p = (glyph < 0);
389 if (glyph == 0) abort();
390 if (glyph < 0) glyph = -glyph;
403 int ccx = ((glyph - 1) % CHAR_COLS);
404 int ccy = ((glyph - 1) / CHAR_COLS);
407 cy = (real_char_rows - ccy - 1) * h;
412 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
413 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
414 brightness *= depth; /* so no row goes all black. */
419 GLfloat r, g, b, a = 1;
424 if (!do_texture && !spinner_p)
425 r = b = 0, g = brightness;
427 r = g = b = brightness;
429 /* If the glyph is very close to the screen (meaning it is very large,
430 and is about to splash into the screen and vanish) then start fading
431 it out, proportional to how close to the glass it is.
433 if (z > GRID_DEPTH/2)
435 GLfloat ratio = ((z - GRID_DEPTH/2) /
436 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
437 int i = ratio * WAVE_SIZE;
440 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
442 a = brightness_ramp[i];
444 /* I don't understand this -- if I change the alpha on the color of
445 the quad, I'd expect that to make the quad more transparent.
446 But instead, it seems to be making the transparent parts of the
447 texture on the quad be *less* transparent! So as we fade out,
448 we fade towards a completely solid rectangle. WTF?
450 So, for now, instead of changing the alpha, just make the colors
451 be darker. This works out ok so long as we use GL_ONE in
452 glBlendFunc, so that stacked glyph colors are added together.
464 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
465 glNormal3f (0, 0, 1);
466 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
467 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
468 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
469 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
472 if (wire && spinner_p)
475 glVertex3f (x, y, z);
476 glVertex3f (x+S, y+S, z);
477 glVertex3f (x, y+S, z);
478 glVertex3f (x+S, y, z);
486 /* Draw all the visible glyphs in the strip.
489 draw_strip (ModeInfo *mi, strip *s)
492 for (i = 0; i < GRID_SIZE; i++)
494 int g = s->glyphs[i];
495 Bool below_p = (s->spinner_y >= i);
500 if (g && below_p) /* don't draw cells below the spinner */
507 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
509 brightness = brightness_ramp[j];
512 draw_glyph (mi, g, s->highlight[i],
513 s->x, s->y - i, s->z, brightness);
518 draw_glyph (mi, s->spinner_glyph, False,
519 s->x, s->y - s->spinner_y, s->z, 1.0);
523 /* qsort comparator for sorting strips by z position */
525 cmp_strips (const void *aa, const void *bb)
527 const strip *a = *(strip **) aa;
528 const strip *b = *(strip **) bb;
529 return ((int) (a->z * 10000) -
530 (int) (b->z * 10000));
538 auto_track_init (ModeInfo *mi)
540 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
543 mp->view_x = nice_views[mp->last_view].x;
544 mp->view_y = nice_views[mp->last_view].y;
545 mp->view_steps = 100;
547 mp->auto_tracking_p = False;
552 auto_track (ModeInfo *mi)
554 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
558 if (mp->button_down_p)
561 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
562 if (! mp->auto_tracking_p)
565 if (++tick < 20/speed) return;
567 if (! (random() % 20))
568 mp->auto_tracking_p = True;
575 GLfloat ox = nice_views[mp->last_view].x;
576 GLfloat oy = nice_views[mp->last_view].y;
577 GLfloat tx = nice_views[mp->target_view].x;
578 GLfloat ty = nice_views[mp->target_view].y;
580 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
582 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
584 mp->view_x = (ox + ((tx - ox) * th));
585 mp->view_y = (oy + ((ty - oy) * th));
588 if (mp->view_tick >= mp->view_steps)
591 mp->view_steps = (350.0 / speed);
592 mp->last_view = mp->target_view;
593 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
594 mp->auto_tracking_p = False;
600 /* Window management, etc
603 reshape_matrix (ModeInfo *mi, int width, int height)
605 GLfloat h = (GLfloat) height / (GLfloat) width;
607 glViewport (0, 0, (GLint) width, (GLint) height);
609 glMatrixMode(GL_PROJECTION);
611 gluPerspective (80.0, 1/h, 1.0, 100);
613 glMatrixMode(GL_MODELVIEW);
615 gluLookAt( 0.0, 0.0, 25.0,
619 glClear(GL_COLOR_BUFFER_BIT);
624 matrix_handle_event (ModeInfo *mi, XEvent *event)
626 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
628 if (event->xany.type == ButtonPress &&
629 event->xbutton.button & Button1)
631 mp->button_down_p = True;
634 else if (event->xany.type == ButtonRelease &&
635 event->xbutton.button & Button1)
637 mp->button_down_p = False;
649 union { int i; char c[sizeof(int)]; } u;
656 /* The image with the characters in it is 512x598, meaning that it needs to
657 be copied into a 512x1024 texture. But some machines can't handle textures
658 that large... And it turns out that we aren't using most of the characters
659 in that image anyway, since this program doesn't do anything that makes use
660 of the full range of Latin1 characters. So... this function tosses out the
661 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
662 can then stuff in a 512x512 texture. Voila.
664 If this hack ever grows into something that displays full Latin1 text,
665 well then, Something Else Will Need To Be Done.
668 spank_image (XImage *xi)
670 int ch = xi->height / CHAR_ROWS;
672 unsigned char *bits = (unsigned char *) xi->data;
673 unsigned char *from, *to, *s, *end;
674 int L = xi->bytes_per_line * ch;
677 /* Copy row 12 into 10 (which really means, copy 2 into 0,
678 since texture data is upside down.).
680 to = bits + (L * cut);
687 /* Then, pull all the bits down by 2 rows.
690 from = bits + (L * cut);
691 end = bits + (L * CHAR_ROWS);
696 /* And clear out the rest, for good measure.
698 from = bits + (L * (CHAR_ROWS - cut));
699 end = bits + (L * CHAR_ROWS);
704 xi->height -= (cut * ch);
705 real_char_rows -= cut;
707 /* Finally, pull the map indexes back to match the new bits.
709 for (i = 0; i < countof(matrix_encoding); i++)
710 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
711 matrix_encoding[i] -= (cut * CHAR_COLS);
716 load_textures (ModeInfo *mi, Bool flip_p)
718 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
724 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
725 So we waste some padding rows to round up.
727 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
731 real_char_rows = CHAR_ROWS;
734 if (xi->height != 512 && xi->height != 1024)
736 xi->height = (xi->height < 512 ? 512 : 1024);
737 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
740 fprintf(stderr, "%s: out of memory\n", progname);
745 if (xi->width != 512) abort();
746 if (xi->height != 512 && xi->height != 1024) abort();
748 /* char size in pixels */
749 cw = orig_w / CHAR_COLS;
750 ch = orig_h / CHAR_ROWS;
752 /* char size in ratio of final (padded) texture size */
753 mp->tex_char_width = (GLfloat) cw / xi->width;
754 mp->tex_char_height = (GLfloat) ch / xi->height;
756 /* Flip each character's bits horizontally -- we could also just do this
757 by reversing the texture coordinates on the quads, but on some systems
758 that slows things down a lot.
763 unsigned long buf[100];
764 for (y = 0; y < xi->height; y++)
765 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
767 for (x = 0; x < cw; x++)
768 buf[x] = XGetPixel (xi, xx+x, y);
769 for (x = 0; x < cw; x++)
770 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
774 /* The pixmap is a color image with no transparency. Set the texture's
775 alpha to be the green channel, and set the green channel to be 100%.
778 int rpos, gpos, bpos, apos; /* bitfield positions */
780 /* #### Cherub says that the little-endian case must be taken on MacOSX,
781 or else the colors/alpha are the wrong way around. How can
785 rpos = 24, gpos = 16, bpos = 8, apos = 0;
788 rpos = 0, gpos = 8, bpos = 16, apos = 24;
790 for (y = 0; y < xi->height; y++)
791 for (x = 0; x < xi->width; x++)
793 unsigned long p = XGetPixel (xi, x, y);
794 unsigned char r = (p >> rpos) & 0xFF;
795 unsigned char g = (p >> gpos) & 0xFF;
796 unsigned char b = (p >> bpos) & 0xFF;
797 unsigned char a = ~g;
799 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
800 XPutPixel (xi, x, y, p);
804 /* Now load the texture into GL.
807 glGenTextures (1, &mp->texture);
809 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
810 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
811 glBindTexture (GL_TEXTURE_2D, mp->texture);
812 check_gl_error ("texture init");
813 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
814 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
817 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
818 check_gl_error (buf);
821 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
822 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
824 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
825 faint solid green border around the texture if it is *not* REPEAT!
827 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
828 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
830 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
831 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
832 check_gl_error ("texture param");
834 xi->data = 0; /* don't free the texture data */
840 init_matrix (ModeInfo *mi)
842 matrix_configuration *mp;
843 int wire = MI_IS_WIREFRAME(mi);
851 mps = (matrix_configuration *)
852 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
854 fprintf(stderr, "%s: out of memory\n", progname);
859 mp = &mps[MI_SCREEN(mi)];
860 mp->glx_context = init_GL(mi);
862 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
865 mp->glyph_map = matrix_encoding;
866 mp->nglyphs = countof(matrix_encoding);
868 else if (!strcasecmp (mode_str, "dna"))
871 mp->glyph_map = dna_encoding;
872 mp->nglyphs = countof(dna_encoding);
874 else if (!strcasecmp (mode_str, "bin") ||
875 !strcasecmp (mode_str, "binary"))
878 mp->glyph_map = binary_encoding;
879 mp->nglyphs = countof(binary_encoding);
881 else if (!strcasecmp (mode_str, "hex") ||
882 !strcasecmp (mode_str, "hexadecimal"))
885 mp->glyph_map = hex_encoding;
886 mp->nglyphs = countof(hex_encoding);
888 else if (!strcasecmp (mode_str, "dec") ||
889 !strcasecmp (mode_str, "decimal"))
892 mp->glyph_map = decimal_encoding;
893 mp->nglyphs = countof(decimal_encoding);
898 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
903 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
905 glShadeModel(GL_SMOOTH);
907 glDisable(GL_DEPTH_TEST);
908 glDisable(GL_CULL_FACE);
909 glEnable(GL_NORMALIZE);
913 load_textures (mi, flip_p);
914 glEnable(GL_TEXTURE_2D);
917 /* Jeff Epler points out:
918 By using GL_ONE instead of GL_SRC_ALPHA, glyphs are added to
919 each other, so that a bright glyph with a darker one in front
920 is a little brighter than the bright glyph alone.
922 glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, /* GL_SRC_ALPHA */ GL_ONE);
925 /* to scale coverage-percent to strips, this number looks about right... */
926 mp->nstrips = (int) (density * 2.2);
927 if (mp->nstrips < 1) mp->nstrips = 1;
928 else if (mp->nstrips > 2000) mp->nstrips = 2000;
931 mp->strips = calloc (mp->nstrips, sizeof(strip));
932 for (i = 0; i < mp->nstrips; i++)
934 strip *s = &mp->strips[i];
937 /* If we start all strips from zero at once, then the first few seconds
938 of the animation are much denser than normal. So instead, set all
939 the initial strips to erase-mode with random starting positions.
940 As these die off at random speeds and are re-created, we'll get a
941 more consistent density. */
943 s->spinner_y = frand(GRID_SIZE);
944 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
947 /* Compute the brightness ramp.
949 for (i = 0; i < WAVE_SIZE; i++)
951 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
952 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
953 j = sin (j); /* j ranges from 0.0 - 1.0 */
954 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
955 brightness_ramp[i] = j;
956 /* printf("%2d %8.2f\n", i, j); */
960 auto_track_init (mi);
967 draw_grid (ModeInfo *mi)
969 if (!MI_IS_WIREFRAME(mi))
971 glDisable(GL_TEXTURE_2D);
977 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
978 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
980 glBegin(GL_LINE_LOOP);
981 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
982 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
983 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
984 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
986 glBegin(GL_LINE_LOOP);
987 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
988 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);
992 glBegin(GL_LINE_LOOP);
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 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
1000 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
1001 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
1002 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
1003 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
1004 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
1005 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
1006 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
1009 if (!MI_IS_WIREFRAME(mi))
1011 glEnable(GL_TEXTURE_2D);
1019 draw_matrix (ModeInfo *mi)
1021 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
1022 Display *dpy = MI_DISPLAY(mi);
1023 Window window = MI_WINDOW(mi);
1026 if (!mp->glx_context)
1029 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1035 glRotatef (mp->view_x, 1, 0, 0);
1036 glRotatef (mp->view_y, 0, 1, 0);
1041 glScalef(0.5, 0.5, 0.5);
1044 glRotatef(-30, 0, 1, 0);
1049 mi->polygon_count = 0;
1051 /* Render (and tick) each strip, starting at the back
1052 (draw the ones farthest from the camera first, to make
1053 the alpha transparency work out right.)
1056 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1057 for (i = 0; i < mp->nstrips; i++)
1058 sorted[i] = &mp->strips[i];
1059 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1061 for (i = 0; i < mp->nstrips; i++)
1063 strip *s = sorted[i];
1075 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1076 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1077 glTexCoord2f (1,1); glVertex3f(15,15,0);
1078 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1084 if (mi->fps_p) do_fps (mi);
1087 glXSwapBuffers(dpy, window);