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 "wscreensaver-glue.h"
34 __extension__ /* don't warn about "string length is greater than the length
35 ISO C89 compilers are required to support" when including
36 the following XPM file... */
38 #include "matrix3.xpm"
41 #define DEF_SPEED "1.0"
42 #define DEF_DENSITY "20"
43 #define DEF_CLOCK "False"
44 #define DEF_FOG "True"
45 #define DEF_WAVES "True"
46 #define DEF_ROTATE "True"
47 #define DEF_TEXTURE "True"
48 #define DEF_MODE "Matrix"
49 #define DEF_TIMEFMT " %l%M%p "
55 static const int matrix_encoding[] = {
56 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
58 192, 193, 194, 195, 196, 197, 198, 199,
59 200, 201, 202, 203, 204, 205, 206, 207
61 160, 161, 162, 163, 164, 165, 166, 167,
62 168, 169, 170, 171, 172, 173, 174, 175
65 static const int decimal_encoding[] = {
66 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
67 static const int hex_encoding[] = {
68 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 };
69 static const int binary_encoding[] = { 16, 17 };
70 static const int dna_encoding[] = { 33, 35, 39, 52 };
72 static const unsigned char char_map[256] = {
73 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */
74 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */
75 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
76 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
77 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
78 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
79 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
80 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
81 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */
82 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */
83 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
84 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
85 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
86 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
88 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
89 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
90 #else /* see spank_image() */
91 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */
92 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */
96 #define CURSOR_GLYPH 97
100 #define GRID_SIZE 70 /* width and height of the arena */
101 #define GRID_DEPTH 35 /* depth of the arena */
102 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
103 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
105 static const struct { GLfloat x, y; } nice_views[] = {
107 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
108 { 0, 20 }, /* every now and then we switch to a new one. */
109 { 25, 0 }, /* (but we only use the first one at start-up.) */
118 { 0, 0 }, /* prefer these */
127 GLfloat x, y, z; /* position of strip */
128 GLfloat dx, dy, dz; /* velocity of strip */
130 Bool erasing_p; /* Whether this strip is on its way out. */
132 int spinner_glyph; /* the bottommost glyph -- the feeder */
133 GLfloat spinner_y; /* where on the strip the bottom glyph is */
134 GLfloat spinner_speed; /* how fast the bottom glyph drops */
136 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
137 revealed by the dropping spinner.
138 0 means no glyph; negative means "spinner".
139 If non-zero, real value is abs(G)-1. */
141 Bool highlight[GRID_SIZE];
142 /* some glyphs may be highlighted */
144 int spin_speed; /* Rotate all spinners every this-many frames */
145 int spin_tick; /* frame counter */
147 int wave_position; /* Waves of brightness wash down the strip. */
148 int wave_speed; /* every this-many frames. */
149 int wave_tick; /* frame counter. */
155 GLXContext *glx_context;
160 const int *glyph_map;
162 GLfloat tex_char_width, tex_char_height;
164 /* auto-tracking direction of view */
165 int last_view, target_view;
166 GLfloat view_x, view_y;
167 int view_steps, view_tick;
168 Bool auto_tracking_p;
172 GLfloat brightness_ramp[WAVE_SIZE];
174 } matrix_configuration;
176 static matrix_configuration *mps = NULL;
178 static GLfloat speed = 1.0;
179 static GLfloat density = 20.0;
180 static Bool do_clock;
181 static char *timefmt;
182 static Bool do_fog = 1;
183 static Bool do_waves;
184 static Bool do_rotate = 1;
185 static Bool do_texture = 1;
186 static char *mode_str;
189 static XrmOptionDescRec opts[] = {
190 { "-speed", ".speed", XrmoptionSepArg, 0 },
191 { "-density", ".density", XrmoptionSepArg, 0 },
192 { "-mode", ".mode", XrmoptionSepArg, 0 },
193 { "-binary", ".mode", XrmoptionNoArg, "binary" },
194 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
195 { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
196 { "-dna", ".mode", XrmoptionNoArg, "dna" },
197 { "-clock", ".clock", XrmoptionNoArg, "True" },
198 { "+clock", ".clock", XrmoptionNoArg, "False" },
199 { "-timefmt", ".timefmt", XrmoptionSepArg, 0 },
200 { "-fog", ".fog", XrmoptionNoArg, "True" },
201 { "+fog", ".fog", XrmoptionNoArg, "False" },
202 { "-waves", ".waves", XrmoptionNoArg, "True" },
203 { "+waves", ".waves", XrmoptionNoArg, "False" },
204 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
205 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
206 {"-texture", ".texture", XrmoptionNoArg, "True" },
207 {"+texture", ".texture", XrmoptionNoArg, "False" },
210 static argtype vars[] = {
211 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
212 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
213 {&density, "density", "Density", DEF_DENSITY, t_Float},
214 {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool},
215 {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String},
216 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
217 {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
218 {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
219 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
222 ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
225 /* Re-randomize the state of one strip.
228 reset_strip (ModeInfo *mi, strip *s)
230 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
232 Bool time_displayed_p = False; /* never display time twice in one strip */
234 memset (s, 0, sizeof(*s));
235 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
236 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
237 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
241 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
243 s->dz = (BELLRAND(0.02) * speed);
245 s->spinner_speed = (BELLRAND(0.3) * speed);
247 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
250 s->wave_position = 0;
251 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
254 for (i = 0; i < GRID_SIZE; i++)
257 (i < GRID_SIZE-5) && /* display approx. once per 5 strips */
258 !(random() % (GRID_SIZE-5)*5))
262 time_t now = time ((time_t *) 0);
263 struct tm *tm = localtime (&now);
264 strftime (text, sizeof(text)-1, timefmt, tm);
266 /* render time into the strip */
267 for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
269 s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
270 s->highlight[i] = True;
273 time_displayed_p = True;
277 int draw_p = (random() % 7);
278 int spin_p = (draw_p && !(random() % 20));
280 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
284 s->highlight[i] = False;
287 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
291 /* Animate the strip one step. Reset if it has reached the bottom.
294 tick_strip (ModeInfo *mi, strip *s)
296 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
299 if (mp->button_down_p)
306 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
312 s->spinner_y += s->spinner_speed;
313 if (s->spinner_y >= GRID_SIZE)
324 s->spinner_speed /= 2; /* erase it slower than we drew it */
328 /* Spin the spinners. */
330 if (s->spin_tick > s->spin_speed)
333 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
334 for (i = 0; i < GRID_SIZE; i++)
335 if (s->glyphs[i] < 0)
337 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
338 if (! (random() % 800)) /* sometimes they stop spinning */
339 s->glyphs[i] = -s->glyphs[i];
343 /* Move the color (brightness) wave. */
345 if (s->wave_tick > s->wave_speed)
349 if (s->wave_position >= WAVE_SIZE)
350 s->wave_position = 0;
355 /* Draw a single character at the given position and brightness.
358 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
359 GLfloat x, GLfloat y, GLfloat z,
362 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
363 int wire = MI_IS_WIREFRAME(mi);
364 GLfloat w = mp->tex_char_width;
365 GLfloat h = mp->tex_char_height;
366 GLfloat cx = 0, cy = 0;
368 Bool spinner_p = (glyph < 0);
370 if (glyph == 0) abort();
371 if (glyph < 0) glyph = -glyph;
384 int ccx = ((glyph - 1) % CHAR_COLS);
385 int ccy = ((glyph - 1) / CHAR_COLS);
388 cy = (mp->real_char_rows - ccy - 1) * h;
393 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
394 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
395 brightness *= depth; /* so no row goes all black. */
405 if (!do_texture && !spinner_p)
412 /* If the glyph is very close to the screen (meaning it is very large,
413 and is about to splash into the screen and vanish) then start fading
414 it out, proportional to how close to the glass it is.
416 if (z > GRID_DEPTH/2)
418 GLfloat ratio = ((z - GRID_DEPTH/2) /
419 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
420 int i = ratio * WAVE_SIZE;
423 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
425 a *= mp->brightness_ramp[i];
431 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
432 glNormal3f (0, 0, 1);
433 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
434 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
435 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
436 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
439 if (wire && spinner_p)
442 glVertex3f (x, y, z);
443 glVertex3f (x+S, y+S, z);
444 glVertex3f (x, y+S, z);
445 glVertex3f (x+S, y, z);
453 /* Draw all the visible glyphs in the strip.
456 draw_strip (ModeInfo *mi, strip *s)
458 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
460 for (i = 0; i < GRID_SIZE; i++)
462 int g = s->glyphs[i];
463 Bool below_p = (s->spinner_y >= i);
468 if (g && below_p) /* don't draw cells below the spinner */
475 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
477 brightness = mp->brightness_ramp[j];
480 draw_glyph (mi, g, s->highlight[i],
481 s->x, s->y - i, s->z, brightness);
486 draw_glyph (mi, s->spinner_glyph, False,
487 s->x, s->y - s->spinner_y, s->z, 1.0);
491 /* qsort comparator for sorting strips by z position */
493 cmp_strips (const void *aa, const void *bb)
495 const strip *a = *(strip **) aa;
496 const strip *b = *(strip **) bb;
497 return ((int) (a->z * 10000) -
498 (int) (b->z * 10000));
506 auto_track_init (ModeInfo *mi)
508 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
511 mp->view_x = nice_views[mp->last_view].x;
512 mp->view_y = nice_views[mp->last_view].y;
513 mp->view_steps = 100;
515 mp->auto_tracking_p = False;
520 auto_track (ModeInfo *mi)
522 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
526 if (mp->button_down_p)
529 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
530 if (! mp->auto_tracking_p)
532 if (++mp->track_tick < 20/speed) return;
534 if (! (random() % 20))
535 mp->auto_tracking_p = True;
542 GLfloat ox = nice_views[mp->last_view].x;
543 GLfloat oy = nice_views[mp->last_view].y;
544 GLfloat tx = nice_views[mp->target_view].x;
545 GLfloat ty = nice_views[mp->target_view].y;
547 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
549 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
551 mp->view_x = (ox + ((tx - ox) * th));
552 mp->view_y = (oy + ((ty - oy) * th));
555 if (mp->view_tick >= mp->view_steps)
558 mp->view_steps = (350.0 / speed);
559 mp->last_view = mp->target_view;
560 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
561 mp->auto_tracking_p = False;
567 /* Window management, etc
570 reshape_matrix (ModeInfo *mi, int width, int height)
572 GLfloat h = (GLfloat) height / (GLfloat) width;
574 glViewport (0, 0, (GLint) width, (GLint) height);
576 glMatrixMode(GL_PROJECTION);
578 gluPerspective (80.0, 1/h, 1.0, 100);
580 glMatrixMode(GL_MODELVIEW);
582 gluLookAt( 0.0, 0.0, 25.0,
590 matrix_handle_event (ModeInfo *mi, XEvent *event)
592 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
594 if (event->xany.type == ButtonPress &&
595 event->xbutton.button == Button1)
597 mp->button_down_p = True;
600 else if (event->xany.type == ButtonRelease &&
601 event->xbutton.button == Button1)
603 mp->button_down_p = False;
615 union { int i; char c[sizeof(int)]; } u;
622 /* The image with the characters in it is 512x598, meaning that it needs to
623 be copied into a 512x1024 texture. But some machines can't handle textures
624 that large... And it turns out that we aren't using most of the characters
625 in that image anyway, since this program doesn't do anything that makes use
626 of the full range of Latin1 characters. So... this function tosses out the
627 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
628 can then stuff in a 512x512 texture. Voila.
630 If this hack ever grows into something that displays full Latin1 text,
631 well then, Something Else Will Need To Be Done.
634 spank_image (matrix_configuration *mp, XImage *xi)
636 int ch = xi->height / CHAR_ROWS;
638 unsigned char *bits = (unsigned char *) xi->data;
639 unsigned char *from, *to, *s, *end;
640 int L = xi->bytes_per_line * ch;
643 /* Copy row 12 into 10 (which really means, copy 2 into 0,
644 since texture data is upside down.).
646 to = bits + (L * cut);
653 /* Then, pull all the bits down by 2 rows.
656 from = bits + (L * cut);
657 end = bits + (L * CHAR_ROWS);
662 /* And clear out the rest, for good measure.
664 from = bits + (L * (CHAR_ROWS - cut));
665 end = bits + (L * CHAR_ROWS);
670 xi->height -= (cut * ch);
671 mp->real_char_rows -= cut;
674 /* Finally, pull the map indexes back to match the new bits.
676 for (i = 0; i < countof(matrix_encoding); i++)
677 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
678 matrix_encoding[i] -= (cut * CHAR_COLS);
684 load_textures (ModeInfo *mi, Bool flip_p)
686 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
692 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
693 So we waste some padding rows to round up.
695 xi = xpm_to_ximage (matrix3_xpm);
698 mp->real_char_rows = CHAR_ROWS;
699 spank_image (mp, xi);
701 if (xi->height != 512 && xi->height != 1024)
703 xi->height = (xi->height < 512 ? 512 : 1024);
704 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
707 fprintf(stderr, "%s: out of memory\n", progname);
712 if (xi->width != 512) abort();
713 if (xi->height != 512 && xi->height != 1024) abort();
715 /* char size in pixels */
716 cw = orig_w / CHAR_COLS;
717 ch = orig_h / CHAR_ROWS;
719 /* char size in ratio of final (padded) texture size */
720 mp->tex_char_width = (GLfloat) cw / xi->width;
721 mp->tex_char_height = (GLfloat) ch / xi->height;
723 /* Flip each character's bits horizontally -- we could also just do this
724 by reversing the texture coordinates on the quads, but on some systems
725 that slows things down a lot.
730 unsigned long buf[100];
731 for (y = 0; y < xi->height; y++)
732 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
734 for (x = 0; x < cw; x++)
735 buf[x] = XGetPixel (xi, xx+x, y);
736 for (x = 0; x < cw; x++)
737 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
741 /* The pixmap is a color image with no transparency. Set the texture's
742 alpha to be the green channel, and set the green channel to be 100%.
745 int rpos, gpos, bpos, apos; /* bitfield positions */
747 /* #### Cherub says that the little-endian case must be taken on MacOSX,
748 or else the colors/alpha are the wrong way around. How can
752 rpos = 24, gpos = 16, bpos = 8, apos = 0;
755 rpos = 0, gpos = 8, bpos = 16, apos = 24;
757 for (y = 0; y < xi->height; y++)
758 for (x = 0; x < xi->width; x++)
760 unsigned long p = XGetPixel (xi, x, y);
761 unsigned char r = (p >> rpos) & 0xFF;
762 unsigned char g = (p >> gpos) & 0xFF;
763 unsigned char b = (p >> bpos) & 0xFF;
766 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
767 XPutPixel (xi, x, y, p);
771 /* Now load the texture into GL.
774 glGenTextures (1, &mp->texture);
776 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
777 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
778 glBindTexture (GL_TEXTURE_2D, mp->texture);
779 check_gl_error ("texture init");
780 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
781 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
784 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
785 check_gl_error (buf);
788 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
789 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
791 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
792 faint solid green border around the texture if it is *not* REPEAT!
794 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
795 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
797 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
798 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
799 check_gl_error ("texture param");
806 init_matrix (ModeInfo *mi)
808 matrix_configuration *mp;
809 int wire = MI_IS_WIREFRAME(mi);
817 mps = (matrix_configuration *)
818 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
820 fprintf(stderr, "%s: out of memory\n", progname);
825 mp = &mps[MI_SCREEN(mi)];
826 mp->glx_context = init_GL(mi);
828 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
831 mp->glyph_map = matrix_encoding;
832 mp->nglyphs = countof(matrix_encoding);
834 else if (!strcasecmp (mode_str, "dna"))
837 mp->glyph_map = dna_encoding;
838 mp->nglyphs = countof(dna_encoding);
840 else if (!strcasecmp (mode_str, "bin") ||
841 !strcasecmp (mode_str, "binary"))
844 mp->glyph_map = binary_encoding;
845 mp->nglyphs = countof(binary_encoding);
847 else if (!strcasecmp (mode_str, "hex") ||
848 !strcasecmp (mode_str, "hexadecimal"))
851 mp->glyph_map = hex_encoding;
852 mp->nglyphs = countof(hex_encoding);
854 else if (!strcasecmp (mode_str, "dec") ||
855 !strcasecmp (mode_str, "decimal"))
858 mp->glyph_map = decimal_encoding;
859 mp->nglyphs = countof(decimal_encoding);
864 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
869 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
871 glShadeModel(GL_SMOOTH);
873 glDisable(GL_DEPTH_TEST);
874 glDisable(GL_CULL_FACE);
875 glEnable(GL_NORMALIZE);
879 load_textures (mi, flip_p);
880 glEnable(GL_TEXTURE_2D);
883 /* Jeff Epler points out:
884 By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
885 added to each other, so that a bright glyph with a darker one
886 in front is a little brighter than the bright glyph alone.
888 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
891 /* to scale coverage-percent to strips, this number looks about right... */
892 mp->nstrips = (int) (density * 2.2);
893 if (mp->nstrips < 1) mp->nstrips = 1;
894 else if (mp->nstrips > 2000) mp->nstrips = 2000;
897 mp->strips = calloc (mp->nstrips, sizeof(strip));
898 for (i = 0; i < mp->nstrips; i++)
900 strip *s = &mp->strips[i];
903 /* If we start all strips from zero at once, then the first few seconds
904 of the animation are much denser than normal. So instead, set all
905 the initial strips to erase-mode with random starting positions.
906 As these die off at random speeds and are re-created, we'll get a
907 more consistent density. */
909 s->spinner_y = frand(GRID_SIZE);
910 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
913 /* Compute the brightness ramp.
915 for (i = 0; i < WAVE_SIZE; i++)
917 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
918 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
919 j = sin (j); /* j ranges from 0.0 - 1.0 */
920 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
921 mp->brightness_ramp[i] = j;
922 /* printf("%2d %8.2f\n", i, j); */
926 auto_track_init (mi);
933 draw_grid (ModeInfo *mi)
935 if (!MI_IS_WIREFRAME(mi))
937 glDisable(GL_TEXTURE_2D);
944 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
945 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
947 glBegin(GL_LINE_LOOP);
948 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
949 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
950 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
951 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
953 glBegin(GL_LINE_LOOP);
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);
957 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
959 glBegin(GL_LINE_LOOP);
960 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
961 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
962 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
963 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
966 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
967 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
968 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
969 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
970 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
971 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
972 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
973 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
976 if (!MI_IS_WIREFRAME(mi))
978 glEnable(GL_TEXTURE_2D);
986 draw_matrix (ModeInfo *mi)
988 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
991 if (!mp->glx_context)
994 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
996 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1002 glRotatef (mp->view_x, 1, 0, 0);
1003 glRotatef (mp->view_y, 0, 1, 0);
1008 glScalef(0.5, 0.5, 0.5);
1011 glRotatef(-30, 0, 1, 0);
1016 mi->polygon_count = 0;
1018 /* Render (and tick) each strip, starting at the back
1019 (draw the ones farthest from the camera first, to make
1020 the alpha transparency work out right.)
1023 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1024 for (i = 0; i < mp->nstrips; i++)
1025 sorted[i] = &mp->strips[i];
1026 qsort (sorted, i, sizeof(*sorted), cmp_strips);
1028 for (i = 0; i < mp->nstrips; i++)
1030 strip *s = sorted[i];
1042 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1043 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1044 glTexCoord2f (1,1); glVertex3f(15,15,0);
1045 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1051 if (mi->fps_p) do_fps (mi);
1054 glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1057 WL_EXPORT struct wscreensaver_plugin glmatrix_screensaver = {