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. */
404 GLfloat r, g, b, a = 1;
409 if (!do_texture && !spinner_p)
410 r = b = 0, g = brightness;
412 r = g = b = brightness;
414 /* If the glyph is very close to the screen (meaning it is very large,
415 and is about to splash into the screen and vanish) then start fading
416 it out, proportional to how close to the glass it is.
418 if (z > GRID_DEPTH/2)
420 GLfloat ratio = ((z - GRID_DEPTH/2) /
421 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
422 int i = ratio * WAVE_SIZE;
425 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
427 a = mp->brightness_ramp[i];
429 /* I don't understand this -- if I change the alpha on the color of
430 the quad, I'd expect that to make the quad more transparent.
431 But instead, it seems to be making the transparent parts of the
432 texture on the quad be *less* transparent! So as we fade out,
433 we fade towards a completely solid rectangle. WTF?
435 So, for now, instead of changing the alpha, just make the colors
436 be darker. This works out ok so long as we use GL_ONE in
437 glBlendFunc, so that stacked glyph colors are added together.
449 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
450 glNormal3f (0, 0, 1);
451 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
452 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
453 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
454 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
457 if (wire && spinner_p)
460 glVertex3f (x, y, z);
461 glVertex3f (x+S, y+S, z);
462 glVertex3f (x, y+S, z);
463 glVertex3f (x+S, y, z);
471 /* Draw all the visible glyphs in the strip.
474 draw_strip (ModeInfo *mi, strip *s)
476 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
478 for (i = 0; i < GRID_SIZE; i++)
480 int g = s->glyphs[i];
481 Bool below_p = (s->spinner_y >= i);
486 if (g && below_p) /* don't draw cells below the spinner */
493 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
495 brightness = mp->brightness_ramp[j];
498 draw_glyph (mi, g, s->highlight[i],
499 s->x, s->y - i, s->z, brightness);
504 draw_glyph (mi, s->spinner_glyph, False,
505 s->x, s->y - s->spinner_y, s->z, 1.0);
509 /* qsort comparator for sorting strips by z position */
511 cmp_strips (const void *aa, const void *bb)
513 const strip *a = *(strip **) aa;
514 const strip *b = *(strip **) bb;
515 return ((int) (a->z * 10000) -
516 (int) (b->z * 10000));
524 auto_track_init (ModeInfo *mi)
526 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
529 mp->view_x = nice_views[mp->last_view].x;
530 mp->view_y = nice_views[mp->last_view].y;
531 mp->view_steps = 100;
533 mp->auto_tracking_p = False;
538 auto_track (ModeInfo *mi)
540 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
544 if (mp->button_down_p)
547 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
548 if (! mp->auto_tracking_p)
550 if (++mp->track_tick < 20/speed) return;
552 if (! (random() % 20))
553 mp->auto_tracking_p = True;
560 GLfloat ox = nice_views[mp->last_view].x;
561 GLfloat oy = nice_views[mp->last_view].y;
562 GLfloat tx = nice_views[mp->target_view].x;
563 GLfloat ty = nice_views[mp->target_view].y;
565 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
567 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
569 mp->view_x = (ox + ((tx - ox) * th));
570 mp->view_y = (oy + ((ty - oy) * th));
573 if (mp->view_tick >= mp->view_steps)
576 mp->view_steps = (350.0 / speed);
577 mp->last_view = mp->target_view;
578 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
579 mp->auto_tracking_p = False;
585 /* Window management, etc
588 reshape_matrix (ModeInfo *mi, int width, int height)
590 GLfloat h = (GLfloat) height / (GLfloat) width;
592 glViewport (0, 0, (GLint) width, (GLint) height);
594 glMatrixMode(GL_PROJECTION);
596 gluPerspective (80.0, 1/h, 1.0, 100);
598 glMatrixMode(GL_MODELVIEW);
600 gluLookAt( 0.0, 0.0, 25.0,
604 glClear(GL_COLOR_BUFFER_BIT);
609 matrix_handle_event (ModeInfo *mi, XEvent *event)
611 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
613 if (event->xany.type == ButtonPress &&
614 event->xbutton.button == Button1)
616 mp->button_down_p = True;
619 else if (event->xany.type == ButtonRelease &&
620 event->xbutton.button == Button1)
622 mp->button_down_p = False;
634 union { int i; char c[sizeof(int)]; } u;
641 /* The image with the characters in it is 512x598, meaning that it needs to
642 be copied into a 512x1024 texture. But some machines can't handle textures
643 that large... And it turns out that we aren't using most of the characters
644 in that image anyway, since this program doesn't do anything that makes use
645 of the full range of Latin1 characters. So... this function tosses out the
646 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
647 can then stuff in a 512x512 texture. Voila.
649 If this hack ever grows into something that displays full Latin1 text,
650 well then, Something Else Will Need To Be Done.
653 spank_image (matrix_configuration *mp, XImage *xi)
655 int ch = xi->height / CHAR_ROWS;
657 unsigned char *bits = (unsigned char *) xi->data;
658 unsigned char *from, *to, *s, *end;
659 int L = xi->bytes_per_line * ch;
662 /* Copy row 12 into 10 (which really means, copy 2 into 0,
663 since texture data is upside down.).
665 to = bits + (L * cut);
672 /* Then, pull all the bits down by 2 rows.
675 from = bits + (L * cut);
676 end = bits + (L * CHAR_ROWS);
681 /* And clear out the rest, for good measure.
683 from = bits + (L * (CHAR_ROWS - cut));
684 end = bits + (L * CHAR_ROWS);
689 xi->height -= (cut * ch);
690 mp->real_char_rows -= cut;
693 /* Finally, pull the map indexes back to match the new bits.
695 for (i = 0; i < countof(matrix_encoding); i++)
696 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
697 matrix_encoding[i] -= (cut * CHAR_COLS);
703 load_textures (ModeInfo *mi, Bool flip_p)
705 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
711 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
712 So we waste some padding rows to round up.
714 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
718 mp->real_char_rows = CHAR_ROWS;
719 spank_image (mp, xi);
721 if (xi->height != 512 && xi->height != 1024)
723 xi->height = (xi->height < 512 ? 512 : 1024);
724 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
727 fprintf(stderr, "%s: out of memory\n", progname);
732 if (xi->width != 512) abort();
733 if (xi->height != 512 && xi->height != 1024) abort();
735 /* char size in pixels */
736 cw = orig_w / CHAR_COLS;
737 ch = orig_h / CHAR_ROWS;
739 /* char size in ratio of final (padded) texture size */
740 mp->tex_char_width = (GLfloat) cw / xi->width;
741 mp->tex_char_height = (GLfloat) ch / xi->height;
743 /* Flip each character's bits horizontally -- we could also just do this
744 by reversing the texture coordinates on the quads, but on some systems
745 that slows things down a lot.
750 unsigned long buf[100];
751 for (y = 0; y < xi->height; y++)
752 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
754 for (x = 0; x < cw; x++)
755 buf[x] = XGetPixel (xi, xx+x, y);
756 for (x = 0; x < cw; x++)
757 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
761 /* The pixmap is a color image with no transparency. Set the texture's
762 alpha to be the green channel, and set the green channel to be 100%.
765 int rpos, gpos, bpos, apos; /* bitfield positions */
767 /* #### Cherub says that the little-endian case must be taken on MacOSX,
768 or else the colors/alpha are the wrong way around. How can
772 rpos = 24, gpos = 16, bpos = 8, apos = 0;
775 rpos = 0, gpos = 8, bpos = 16, apos = 24;
777 for (y = 0; y < xi->height; y++)
778 for (x = 0; x < xi->width; x++)
780 unsigned long p = XGetPixel (xi, x, y);
781 unsigned char r = (p >> rpos) & 0xFF;
782 unsigned char g = (p >> gpos) & 0xFF;
783 unsigned char b = (p >> bpos) & 0xFF;
784 unsigned char a = ~g;
786 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
787 XPutPixel (xi, x, y, p);
791 /* Now load the texture into GL.
794 glGenTextures (1, &mp->texture);
796 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
797 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
798 glBindTexture (GL_TEXTURE_2D, mp->texture);
799 check_gl_error ("texture init");
800 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
801 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
804 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
805 check_gl_error (buf);
808 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
809 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
811 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
812 faint solid green border around the texture if it is *not* REPEAT!
814 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
815 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
817 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
818 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
819 check_gl_error ("texture param");
826 init_matrix (ModeInfo *mi)
828 matrix_configuration *mp;
829 int wire = MI_IS_WIREFRAME(mi);
837 mps = (matrix_configuration *)
838 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
840 fprintf(stderr, "%s: out of memory\n", progname);
845 mp = &mps[MI_SCREEN(mi)];
846 mp->glx_context = init_GL(mi);
848 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
851 mp->glyph_map = matrix_encoding;
852 mp->nglyphs = countof(matrix_encoding);
854 else if (!strcasecmp (mode_str, "dna"))
857 mp->glyph_map = dna_encoding;
858 mp->nglyphs = countof(dna_encoding);
860 else if (!strcasecmp (mode_str, "bin") ||
861 !strcasecmp (mode_str, "binary"))
864 mp->glyph_map = binary_encoding;
865 mp->nglyphs = countof(binary_encoding);
867 else if (!strcasecmp (mode_str, "hex") ||
868 !strcasecmp (mode_str, "hexadecimal"))
871 mp->glyph_map = hex_encoding;
872 mp->nglyphs = countof(hex_encoding);
874 else if (!strcasecmp (mode_str, "dec") ||
875 !strcasecmp (mode_str, "decimal"))
878 mp->glyph_map = decimal_encoding;
879 mp->nglyphs = countof(decimal_encoding);
884 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
889 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
891 glShadeModel(GL_SMOOTH);
893 glDisable(GL_DEPTH_TEST);
894 glDisable(GL_CULL_FACE);
895 glEnable(GL_NORMALIZE);
899 load_textures (mi, flip_p);
900 glEnable(GL_TEXTURE_2D);
903 /* Jeff Epler points out:
904 By using GL_ONE instead of GL_SRC_ALPHA, glyphs are added to
905 each other, so that a bright glyph with a darker one in front
906 is a little brighter than the bright glyph alone.
908 glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, /* GL_SRC_ALPHA */ GL_ONE);
911 /* to scale coverage-percent to strips, this number looks about right... */
912 mp->nstrips = (int) (density * 2.2);
913 if (mp->nstrips < 1) mp->nstrips = 1;
914 else if (mp->nstrips > 2000) mp->nstrips = 2000;
917 mp->strips = calloc (mp->nstrips, sizeof(strip));
918 for (i = 0; i < mp->nstrips; i++)
920 strip *s = &mp->strips[i];
923 /* If we start all strips from zero at once, then the first few seconds
924 of the animation are much denser than normal. So instead, set all
925 the initial strips to erase-mode with random starting positions.
926 As these die off at random speeds and are re-created, we'll get a
927 more consistent density. */
929 s->spinner_y = frand(GRID_SIZE);
930 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
933 /* Compute the brightness ramp.
935 for (i = 0; i < WAVE_SIZE; i++)
937 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
938 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
939 j = sin (j); /* j ranges from 0.0 - 1.0 */
940 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
941 mp->brightness_ramp[i] = j;
942 /* printf("%2d %8.2f\n", i, j); */
946 auto_track_init (mi);
953 draw_grid (ModeInfo *mi)
955 if (!MI_IS_WIREFRAME(mi))
957 glDisable(GL_TEXTURE_2D);
964 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
965 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
967 glBegin(GL_LINE_LOOP);
968 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
969 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
970 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
971 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
973 glBegin(GL_LINE_LOOP);
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);
979 glBegin(GL_LINE_LOOP);
980 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
981 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
982 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
983 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
986 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
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);
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);
996 if (!MI_IS_WIREFRAME(mi))
998 glEnable(GL_TEXTURE_2D);
1006 draw_matrix (ModeInfo *mi)
1008 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
1009 Display *dpy = MI_DISPLAY(mi);
1010 Window window = MI_WINDOW(mi);
1013 if (!mp->glx_context)
1016 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
1018 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1024 glRotatef (mp->view_x, 1, 0, 0);
1025 glRotatef (mp->view_y, 0, 1, 0);
1030 glScalef(0.5, 0.5, 0.5);
1033 glRotatef(-30, 0, 1, 0);
1038 mi->polygon_count = 0;
1040 /* Render (and tick) each strip, starting at the back
1041 (draw the ones farthest from the camera first, to make
1042 the alpha transparency work out right.)
1045 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1046 for (i = 0; i < mp->nstrips; i++)
1047 sorted[i] = &mp->strips[i];
1048 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1050 for (i = 0; i < mp->nstrips; i++)
1052 strip *s = sorted[i];
1064 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1065 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1066 glTexCoord2f (1,1); glVertex3f(15,15,0);
1067 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1073 if (mi->fps_p) do_fps (mi);
1076 glXSwapBuffers(dpy, window);
1079 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)