From http://www.jwz.org/xscreensaver/xscreensaver-5.15.tar.gz
[xscreensaver] / hacks / glx / glmatrix.c
1 /* glmatrix, Copyright (c) 2003, 2004 Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  *
11  * GLMatrix -- simulate the text scrolls from the movie "The Matrix".
12  *
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
16  * movie did.
17  */
18
19 #define DEFAULTS        "*delay:        30000         \n" \
20                         "*showFPS:      False         \n" \
21                         "*wireframe:    False         \n" \
22
23 # define refresh_matrix 0
24 # define release_matrix 0
25 #undef countof
26 #define countof(x) (sizeof((x))/sizeof((*x)))
27
28 #undef BELLRAND
29 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
30
31 #include "xlockmore.h"
32 #include "xpm-ximage.h"
33
34 #ifdef __GNUC__
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... */
38 #endif
39 #include "../images/matrix3.xpm"
40
41 #ifdef USE_GL /* whole file */
42
43
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 "
53
54
55 #define CHAR_COLS 16
56 #define CHAR_ROWS 13
57
58 static const int matrix_encoding[] = {
59     16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
60 # if 0
61     192, 193, 194, 195, 196, 197, 198, 199,
62     200, 201, 202, 203, 204, 205, 206, 207
63 # else
64     160, 161, 162, 163, 164, 165, 166, 167,
65     168, 169, 170, 171, 172, 173, 174, 175
66 # endif
67   };
68 static const int decimal_encoding[]  = {
69   16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
70 static const int hex_encoding[] = {
71   16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 };
72 static const int binary_encoding[] = { 16, 17 };
73 static const int dna_encoding[]    = { 33, 35, 39, 52 };
74
75 static const unsigned char char_map[256] = {
76    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /*   0 */
77    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /*  16 */
78     0,  1,  2, 96,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  /*  32 */
79    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,  /*  48 */
80    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,  /*  64 */
81    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,  /*  80 */
82    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  /*  96 */
83    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,  /* 112 */
84    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 128 */
85    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 144 */
86    96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,  /* 160 */
87   112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,  /* 176 */
88   128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,  /* 192 */
89   144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,  /* 208 */
90 #if 0
91   160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,  /* 224 */
92   176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191   /* 240 */
93 #else /* see spank_image() */
94    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 224 */
95    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 240 */
96 #endif
97 };
98
99 #define CURSOR_GLYPH 97
100
101 /* #define DEBUG */
102
103 #define GRID_SIZE  70     /* width and height of the arena */
104 #define GRID_DEPTH 35     /* depth of the arena */
105 #define WAVE_SIZE  22     /* periodicity of color (brightness) waves */
106 #define SPLASH_RATIO 0.7  /* ratio of GRID_DEPTH where chars hit the screen */
107
108 static const struct { GLfloat x, y; } nice_views[] = {
109   {  0,     0 },
110   {  0,   -20 },     /* this is a list of viewer rotations that look nice. */
111   {  0,    20 },     /* every now and then we switch to a new one.         */
112   { 25,     0 },     /* (but we only use the first one at start-up.)       */
113   {-25,     0 },
114   { 25,    20 },
115   {-25,    20 },
116   { 25,   -20 },
117   {-25,   -20 },
118
119   { 10,     0 },
120   {-10,     0 },
121   {  0,     0 },  /* prefer these */
122   {  0,     0 },
123   {  0,     0 },
124   {  0,     0 },
125   {  0,     0 },
126 };
127
128
129 typedef struct {
130   GLfloat x, y, z;        /* position of strip */
131   GLfloat dx, dy, dz;     /* velocity of strip */
132
133   Bool erasing_p;         /* Whether this strip is on its way out. */
134
135   int spinner_glyph;      /* the bottommost glyph -- the feeder */
136   GLfloat spinner_y;      /* where on the strip the bottom glyph is */
137   GLfloat spinner_speed;  /* how fast the bottom glyph drops */
138
139   int glyphs[GRID_SIZE];  /* the other glyphs on the strip, which will be
140                              revealed by the dropping spinner.
141                              0 means no glyph; negative means "spinner".
142                              If non-zero, real value is abs(G)-1. */
143
144   Bool highlight[GRID_SIZE];
145                           /* some glyphs may be highlighted */
146   
147   int spin_speed;         /* Rotate all spinners every this-many frames */
148   int spin_tick;          /* frame counter */
149
150   int wave_position;      /* Waves of brightness wash down the strip. */
151   int wave_speed;         /* every this-many frames. */
152   int wave_tick;          /* frame counter. */
153
154 } strip;
155
156
157 typedef struct {
158   GLXContext *glx_context;
159   Bool button_down_p;
160   GLuint texture;
161   int nstrips;
162   strip *strips;
163   const int *glyph_map;
164   int nglyphs;
165   GLfloat tex_char_width, tex_char_height;
166
167   /* auto-tracking direction of view */
168   int last_view, target_view;
169   GLfloat view_x, view_y;
170   int view_steps, view_tick;
171   Bool auto_tracking_p;
172   int track_tick;
173
174   int real_char_rows;
175   GLfloat brightness_ramp[WAVE_SIZE];
176
177 } matrix_configuration;
178
179 static matrix_configuration *mps = NULL;
180
181 static GLfloat speed;
182 static GLfloat density;
183 static Bool do_clock;
184 static char *timefmt;
185 static Bool do_fog;
186 static Bool do_waves;
187 static Bool do_rotate;
188 static Bool do_texture;
189 static char *mode_str;
190
191 static XrmOptionDescRec opts[] = {
192   { "-speed",       ".speed",     XrmoptionSepArg, 0 },
193   { "-density",     ".density",   XrmoptionSepArg, 0 },
194   { "-mode",        ".mode",      XrmoptionSepArg, 0 },
195   { "-binary",      ".mode",      XrmoptionNoArg, "binary"      },
196   { "-hexadecimal", ".mode",      XrmoptionNoArg, "hexadecimal" },
197   { "-decimal",     ".mode",      XrmoptionNoArg, "decimal"     },
198   { "-dna",         ".mode",      XrmoptionNoArg, "dna"         },
199   { "-clock",       ".clock",     XrmoptionNoArg, "True"  },
200   { "+clock",       ".clock",     XrmoptionNoArg, "False" },
201   { "-timefmt",     ".timefmt",   XrmoptionSepArg, 0  },
202   { "-fog",         ".fog",       XrmoptionNoArg, "True"  },
203   { "+fog",         ".fog",       XrmoptionNoArg, "False" },
204   { "-waves",       ".waves",     XrmoptionNoArg, "True"  },
205   { "+waves",       ".waves",     XrmoptionNoArg, "False" },
206   { "-rotate",      ".rotate",    XrmoptionNoArg, "True"  },
207   { "+rotate",      ".rotate",    XrmoptionNoArg, "False" },
208   {"-texture",      ".texture",   XrmoptionNoArg, "True"  },
209   {"+texture",      ".texture",   XrmoptionNoArg, "False" },
210 };
211
212 static argtype vars[] = {
213   {&mode_str,   "mode",       "Mode",    DEF_MODE,      t_String},
214   {&speed,      "speed",      "Speed",   DEF_SPEED,     t_Float},
215   {&density,    "density",    "Density", DEF_DENSITY,   t_Float},
216   {&do_clock,   "clock",      "Clock",   DEF_CLOCK,     t_Bool},
217   {&timefmt,    "timefmt",    "Timefmt", DEF_TIMEFMT,   t_String},
218   {&do_fog,     "fog",        "Fog",     DEF_FOG,       t_Bool},
219   {&do_waves,   "waves",      "Waves",   DEF_WAVES,     t_Bool},
220   {&do_rotate,  "rotate",     "Rotate",  DEF_ROTATE,    t_Bool},
221   {&do_texture, "texture",    "Texture", DEF_TEXTURE,   t_Bool},
222 };
223
224 ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
225
226
227 /* Re-randomize the state of one strip.
228  */
229 static void
230 reset_strip (ModeInfo *mi, strip *s)
231 {
232   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
233   int i;
234   Bool time_displayed_p = False;  /* never display time twice in one strip */
235
236   memset (s, 0, sizeof(*s));
237   s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
238   s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5));      /* shift top slightly */
239   s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
240   s->spinner_y = 0;
241
242   s->dx = 0;
243 /*  s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
244   s->dy = 0;
245   s->dz = (BELLRAND(0.02) * speed);
246
247   s->spinner_speed = (BELLRAND(0.3) * speed);
248
249   s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
250   s->spin_tick  = 0;
251
252   s->wave_position = 0;
253   s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
254   s->wave_tick  = 0;
255
256   for (i = 0; i < GRID_SIZE; i++)
257     if (do_clock &&
258         !time_displayed_p &&
259         (i < GRID_SIZE-5) &&   /* display approx. once per 5 strips */
260         !(random() % (GRID_SIZE-5)*5))
261       {
262         int j;
263         char text[80];
264         time_t now = time ((time_t *) 0);
265         struct tm *tm = localtime (&now);
266         strftime (text, sizeof(text)-1, timefmt, tm);
267
268         /* render time into the strip */
269         for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
270           {
271             s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
272             s->highlight[i] = True;
273           }
274
275         time_displayed_p = True;        
276       }
277     else
278       {
279         int draw_p = (random() % 7);
280         int spin_p = (draw_p && !(random() % 20));
281         int g = (draw_p
282                  ? mp->glyph_map[(random() % mp->nglyphs)] + 1
283                  : 0);
284         if (spin_p) g = -g;
285         s->glyphs[i] = g;
286         s->highlight[i] = False;
287       }
288
289   s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
290 }
291
292
293 /* Animate the strip one step.  Reset if it has reached the bottom.
294  */
295 static void
296 tick_strip (ModeInfo *mi, strip *s)
297 {
298   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
299   int i;
300
301   if (mp->button_down_p)
302     return;
303
304   s->x += s->dx;
305   s->y += s->dy;
306   s->z += s->dz;
307
308   if (s->z > GRID_DEPTH * SPLASH_RATIO)  /* splashed into screen */
309     {
310       reset_strip (mi, s);
311       return;
312     }
313
314   s->spinner_y += s->spinner_speed;
315   if (s->spinner_y >= GRID_SIZE)
316     {
317       if (s->erasing_p)
318         {
319           reset_strip (mi, s);
320           return;
321         }
322       else
323         {
324           s->erasing_p = True;
325           s->spinner_y = 0;
326           s->spinner_speed /= 2;  /* erase it slower than we drew it */
327         }
328     }
329
330   /* Spin the spinners. */
331   s->spin_tick++;
332   if (s->spin_tick > s->spin_speed)
333     {
334       s->spin_tick = 0;
335       s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
336       for (i = 0; i < GRID_SIZE; i++)
337         if (s->glyphs[i] < 0)
338           {
339             s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
340             if (! (random() % 800))  /* sometimes they stop spinning */
341               s->glyphs[i] = -s->glyphs[i];
342           }
343     }
344
345   /* Move the color (brightness) wave. */
346   s->wave_tick++;
347   if (s->wave_tick > s->wave_speed)
348     {
349       s->wave_tick = 0;
350       s->wave_position++;
351       if (s->wave_position >= WAVE_SIZE)
352         s->wave_position = 0;
353     }
354 }
355
356
357 /* Draw a single character at the given position and brightness.
358  */
359 static void
360 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
361             GLfloat x, GLfloat y, GLfloat z,
362             GLfloat brightness)
363 {
364   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
365   int wire = MI_IS_WIREFRAME(mi);
366   GLfloat w = mp->tex_char_width;
367   GLfloat h = mp->tex_char_height;
368   GLfloat cx = 0, cy = 0;
369   GLfloat S = 1;
370   Bool spinner_p = (glyph < 0);
371
372   if (glyph == 0) abort();
373   if (glyph < 0) glyph = -glyph;
374
375   if (spinner_p)
376     brightness *= 1.5;
377
378   if (!do_texture)
379     {
380       S  = 0.8;
381       x += 0.1;
382       y += 0.1;
383     }
384   else
385     {
386       int ccx = ((glyph - 1) % CHAR_COLS);
387       int ccy = ((glyph - 1) / CHAR_COLS);
388
389       cx = ccx * w;
390       cy = (mp->real_char_rows - ccy - 1) * h;
391
392       if (do_fog)
393         {
394           GLfloat depth;
395           depth = (z / GRID_DEPTH) + 0.5;  /* z ratio from back/front      */
396           depth = 0.2 + (depth * 0.8);     /* scale to range [0.2 - 1.0]   */
397           brightness *= depth;             /* so no row goes all black.    */
398         }
399     }
400
401   {
402     GLfloat r, g, b, a;
403
404     if (highlight)
405       brightness *= 2;
406
407     if (!do_texture && !spinner_p)
408       r = b = 0, g = 1;
409     else
410       r = g = b = 1;
411
412     a = brightness;
413
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.
417     */
418     if (z > GRID_DEPTH/2)
419       {
420         GLfloat ratio = ((z - GRID_DEPTH/2) /
421                          ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
422         int i = ratio * WAVE_SIZE;
423
424         if (i < 0) i = 0;
425         else if (i >= WAVE_SIZE) i = WAVE_SIZE-1; 
426
427         a *= mp->brightness_ramp[i];
428       }
429
430     glColor4f (r,g,b,a);
431   }
432
433   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
434   glNormal3f (0, 0, 1);
435   glTexCoord2f (cx,   cy);   glVertex3f (x,   y,   z);
436   glTexCoord2f (cx+w, cy);   glVertex3f (x+S, y,   z);
437   glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
438   glTexCoord2f (cx,   cy+h); glVertex3f (x,   y+S, z);
439   glEnd ();
440
441   if (wire && spinner_p)
442     {
443       glBegin (GL_LINES);
444       glVertex3f (x,   y,   z);
445       glVertex3f (x+S, y+S, z);
446       glVertex3f (x,   y+S, z);
447       glVertex3f (x+S, y,   z);
448       glEnd();
449     }
450
451   mi->polygon_count++;
452 }
453
454
455 /* Draw all the visible glyphs in the strip.
456  */
457 static void
458 draw_strip (ModeInfo *mi, strip *s)
459 {
460   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
461   int i;
462   for (i = 0; i < GRID_SIZE; i++)
463     {
464       int g = s->glyphs[i];
465       Bool below_p = (s->spinner_y >= i);
466
467       if (s->erasing_p)
468         below_p = !below_p;
469
470       if (g && below_p)       /* don't draw cells below the spinner */
471         {
472           GLfloat brightness;
473           if (!do_waves)
474             brightness = 1.0;
475           else
476             {
477               int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
478                                    % WAVE_SIZE);
479               brightness = mp->brightness_ramp[j];
480             }
481
482           draw_glyph (mi, g, s->highlight[i],
483                       s->x, s->y - i, s->z, brightness);
484         }
485     }
486
487   if (!s->erasing_p)
488     draw_glyph (mi, s->spinner_glyph, False,
489                 s->x, s->y - s->spinner_y, s->z, 1.0);
490 }
491
492
493 /* qsort comparator for sorting strips by z position */
494 static int
495 cmp_strips (const void *aa, const void *bb)
496 {
497   const strip *a = *(strip **) aa;
498   const strip *b = *(strip **) bb;
499   return ((int) (a->z * 10000) -
500           (int) (b->z * 10000));
501 }
502
503
504 /* Auto-tracking
505  */
506
507 static void
508 auto_track_init (ModeInfo *mi)
509 {
510   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
511   mp->last_view = 0;
512   mp->target_view = 0;
513   mp->view_x = nice_views[mp->last_view].x;
514   mp->view_y = nice_views[mp->last_view].y;
515   mp->view_steps = 100;
516   mp->view_tick = 0;
517   mp->auto_tracking_p = False;
518 }
519
520
521 static void
522 auto_track (ModeInfo *mi)
523 {
524   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
525
526   if (! do_rotate)
527     return;
528   if (mp->button_down_p)
529     return;
530
531   /* if we're not moving, maybe start moving.  Otherwise, do nothing. */
532   if (! mp->auto_tracking_p)
533     {
534       if (++mp->track_tick < 20/speed) return;
535       mp->track_tick = 0;
536       if (! (random() % 20))
537         mp->auto_tracking_p = True;
538       else
539         return;
540     }
541
542
543   {
544     GLfloat ox = nice_views[mp->last_view].x;
545     GLfloat oy = nice_views[mp->last_view].y;
546     GLfloat tx = nice_views[mp->target_view].x;
547     GLfloat ty = nice_views[mp->target_view].y;
548
549     /* move from A to B with sinusoidal deltas, so that it doesn't jerk
550        to a stop. */
551     GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
552
553     mp->view_x = (ox + ((tx - ox) * th));
554     mp->view_y = (oy + ((ty - oy) * th));
555     mp->view_tick++;
556
557   if (mp->view_tick >= mp->view_steps)
558     {
559       mp->view_tick = 0;
560       mp->view_steps = (350.0 / speed);
561       mp->last_view = mp->target_view;
562       mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
563       mp->auto_tracking_p = False;
564     }
565   }
566 }
567
568
569 /* Window management, etc
570  */
571 ENTRYPOINT void
572 reshape_matrix (ModeInfo *mi, int width, int height)
573 {
574   GLfloat h = (GLfloat) height / (GLfloat) width;
575
576   glViewport (0, 0, (GLint) width, (GLint) height);
577
578   glMatrixMode(GL_PROJECTION);
579   glLoadIdentity();
580   gluPerspective (80.0, 1/h, 1.0, 100);
581
582   glMatrixMode(GL_MODELVIEW);
583   glLoadIdentity();
584   gluLookAt( 0.0, 0.0, 25.0,
585              0.0, 0.0, 0.0,
586              0.0, 1.0, 0.0);
587
588   glClear(GL_COLOR_BUFFER_BIT);
589 }
590
591
592 ENTRYPOINT Bool
593 matrix_handle_event (ModeInfo *mi, XEvent *event)
594 {
595   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
596
597   if (event->xany.type == ButtonPress &&
598       event->xbutton.button == Button1)
599     {
600       mp->button_down_p = True;
601       return True;
602     }
603   else if (event->xany.type == ButtonRelease &&
604            event->xbutton.button == Button1)
605     {
606       mp->button_down_p = False;
607       return True;
608     }
609
610   return False;
611 }
612
613
614 #if 0
615 static Bool
616 bigendian (void)
617 {
618   union { int i; char c[sizeof(int)]; } u;
619   u.i = 1;
620   return !u.c[0];
621 }
622 #endif
623
624
625 /* The image with the characters in it is 512x598, meaning that it needs to
626    be copied into a 512x1024 texture.  But some machines can't handle textures
627    that large...  And it turns out that we aren't using most of the characters
628    in that image anyway, since this program doesn't do anything that makes use
629    of the full range of Latin1 characters.  So... this function tosses out the
630    last 32 of the Latin1 characters, resulting in a 512x506 image, which we
631    can then stuff in a 512x512 texture.  Voila.
632
633    If this hack ever grows into something that displays full Latin1 text,
634    well then, Something Else Will Need To Be Done.
635  */
636 static void
637 spank_image (matrix_configuration *mp, XImage *xi)
638 {
639   int ch = xi->height / CHAR_ROWS;
640   int cut = 2;
641   unsigned char *bits = (unsigned char *) xi->data;
642   unsigned char *from, *to, *s, *end;
643   int L = xi->bytes_per_line * ch;
644 /*  int i;*/
645
646   /* Copy row 12 into 10 (which really means, copy 2 into 0,
647      since texture data is upside down.).
648   */
649   to   = bits + (L * cut);
650   from = bits;
651   end  = from + L;
652   s    = from;
653   while (s < end)
654     *to++ = *s++;
655
656   /* Then, pull all the bits down by 2 rows.
657    */
658   to   = bits;
659   from = bits + (L * cut);
660   end  = bits + (L * CHAR_ROWS);
661   s    = from;
662   while (s < end)
663     *to++ = *s++;
664
665   /* And clear out the rest, for good measure.
666    */
667   from = bits + (L * (CHAR_ROWS - cut));
668   end  = bits + (L * CHAR_ROWS);
669   s    = from;
670   while (s < end)
671     *s++ = 0;
672
673   xi->height -= (cut * ch);
674   mp->real_char_rows -= cut;
675
676 # if 0
677   /* Finally, pull the map indexes back to match the new bits.
678    */
679   for (i = 0; i < countof(matrix_encoding); i++)
680     if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
681       matrix_encoding[i] -= (cut * CHAR_COLS);
682 # endif
683 }
684
685
686 static void
687 load_textures (ModeInfo *mi, Bool flip_p)
688 {
689   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
690   XImage *xi;
691   int x, y;
692   int cw, ch;
693   int orig_w, orig_h;
694
695   /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
696      So we waste some padding rows to round up.
697    */
698   xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
699                       matrix3_xpm);
700   orig_w = xi->width;
701   orig_h = xi->height;
702   mp->real_char_rows = CHAR_ROWS;
703   spank_image (mp, xi);
704
705   if (xi->height != 512 && xi->height != 1024)
706     {
707       xi->height = (xi->height < 512 ? 512 : 1024);
708       xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
709       if (!xi->data)
710         {
711           fprintf(stderr, "%s: out of memory\n", progname);
712           exit(1);
713         }
714     }
715
716   if (xi->width != 512) abort();
717   if (xi->height != 512 && xi->height != 1024) abort();
718
719   /* char size in pixels */
720   cw = orig_w / CHAR_COLS;
721   ch = orig_h / CHAR_ROWS;
722
723   /* char size in ratio of final (padded) texture size */
724   mp->tex_char_width  = (GLfloat) cw / xi->width;
725   mp->tex_char_height = (GLfloat) ch / xi->height;
726
727   /* Flip each character's bits horizontally -- we could also just do this
728      by reversing the texture coordinates on the quads, but on some systems
729      that slows things down a lot.
730    */
731   if (flip_p)
732     {
733       int xx, col;
734       unsigned long buf[100];
735       for (y = 0; y < xi->height; y++)
736         for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
737           {
738             for (x = 0; x < cw; x++)
739               buf[x] = XGetPixel (xi, xx+x, y);
740             for (x = 0; x < cw; x++)
741               XPutPixel (xi, xx+x, y, buf[cw-x-1]);
742           }
743     }
744
745   /* The pixmap is a color image with no transparency.  Set the texture's
746      alpha to be the green channel, and set the green channel to be 100%.
747    */
748   {
749     int rpos, gpos, bpos, apos;  /* bitfield positions */
750 #if 0
751     /* #### Cherub says that the little-endian case must be taken on MacOSX,
752             or else the colors/alpha are the wrong way around.  How can
753             that be the case?
754      */
755     if (bigendian())
756       rpos = 24, gpos = 16, bpos =  8, apos =  0;
757     else
758 #endif
759       rpos =  0, gpos =  8, bpos = 16, apos = 24;
760
761     for (y = 0; y < xi->height; y++)
762       for (x = 0; x < xi->width; x++)
763         {
764           unsigned long p = XGetPixel (xi, x, y);
765           unsigned char r = (p >> rpos) & 0xFF;
766           unsigned char g = (p >> gpos) & 0xFF;
767           unsigned char b = (p >> bpos) & 0xFF;
768           unsigned char a = g;
769           g = 0xFF;
770           p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
771           XPutPixel (xi, x, y, p);
772         }
773   }
774
775   /* Now load the texture into GL.
776    */
777   clear_gl_error();
778   glGenTextures (1, &mp->texture);
779
780   glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
781   glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
782   glBindTexture (GL_TEXTURE_2D, mp->texture);
783   check_gl_error ("texture init");
784   glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
785                 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
786   {
787     char buf[255];
788     sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
789     check_gl_error (buf);
790   }
791
792   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
793   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
794
795   /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
796      faint solid green border around the texture if it is *not* REPEAT!
797   */
798   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
799   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
800
801   glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
802   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
803   check_gl_error ("texture param");
804
805   XDestroyImage (xi);
806 }
807
808
809 ENTRYPOINT void 
810 init_matrix (ModeInfo *mi)
811 {
812   matrix_configuration *mp;
813   int wire = MI_IS_WIREFRAME(mi);
814   Bool flip_p = 0;
815   int i;
816
817   if (wire)
818     do_texture = False;
819
820   if (!mps) {
821     mps = (matrix_configuration *)
822       calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
823     if (!mps) {
824       fprintf(stderr, "%s: out of memory\n", progname);
825       exit(1);
826     }
827   }
828
829   mp = &mps[MI_SCREEN(mi)];
830   mp->glx_context = init_GL(mi);
831
832   if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
833     {
834       flip_p = 1;
835       mp->glyph_map = matrix_encoding;
836       mp->nglyphs   = countof(matrix_encoding);
837     }
838   else if (!strcasecmp (mode_str, "dna"))
839     {
840       flip_p = 0;
841       mp->glyph_map = dna_encoding;
842       mp->nglyphs   = countof(dna_encoding);
843     }
844   else if (!strcasecmp (mode_str, "bin") ||
845            !strcasecmp (mode_str, "binary"))
846     {
847       flip_p = 0;
848       mp->glyph_map = binary_encoding;
849       mp->nglyphs   = countof(binary_encoding);
850     }
851   else if (!strcasecmp (mode_str, "hex") ||
852            !strcasecmp (mode_str, "hexadecimal"))
853     {
854       flip_p = 0;
855       mp->glyph_map = hex_encoding;
856       mp->nglyphs   = countof(hex_encoding);
857     }
858   else if (!strcasecmp (mode_str, "dec") ||
859            !strcasecmp (mode_str, "decimal"))
860     {
861       flip_p = 0;
862       mp->glyph_map = decimal_encoding;
863       mp->nglyphs   = countof(decimal_encoding);
864     }
865   else
866     {
867       fprintf (stderr,
868            "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
869                progname, mode_str);
870       exit (1);
871     }
872
873   reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
874
875   glShadeModel(GL_SMOOTH);
876
877   glDisable(GL_DEPTH_TEST);
878   glDisable(GL_CULL_FACE);
879   glEnable(GL_NORMALIZE);
880
881   if (do_texture)
882     {
883       load_textures (mi, flip_p);
884       glEnable(GL_TEXTURE_2D);
885       glEnable(GL_BLEND);
886
887       /* Jeff Epler points out:
888          By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
889          added to each other, so that a bright glyph with a darker one
890          in front is a little brighter than the bright glyph alone.
891        */
892       glBlendFunc (GL_SRC_ALPHA, GL_ONE);
893     }
894
895   /* to scale coverage-percent to strips, this number looks about right... */
896   mp->nstrips = (int) (density * 2.2);
897   if      (mp->nstrips < 1)    mp->nstrips = 1;
898   else if (mp->nstrips > 2000) mp->nstrips = 2000;
899
900
901   mp->strips = calloc (mp->nstrips, sizeof(strip));
902   for (i = 0; i < mp->nstrips; i++)
903     {
904       strip *s = &mp->strips[i];
905       reset_strip (mi, s);
906
907       /* If we start all strips from zero at once, then the first few seconds
908          of the animation are much denser than normal.  So instead, set all
909          the initial strips to erase-mode with random starting positions.
910          As these die off at random speeds and are re-created, we'll get a
911          more consistent density. */
912       s->erasing_p = True;
913       s->spinner_y = frand(GRID_SIZE);
914       memset (s->glyphs, 0, sizeof(s->glyphs));  /* no visible glyphs */
915     }
916
917   /* Compute the brightness ramp.
918    */
919   for (i = 0; i < WAVE_SIZE; i++)
920     {
921       GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
922       j *= (M_PI / 2);       /* j ranges from 0.0 - PI/2  */
923       j = sin (j);           /* j ranges from 0.0 - 1.0   */
924       j = 0.2 + (j * 0.8);   /* j ranges from 0.2 - 1.0   */
925       mp->brightness_ramp[i] = j;
926       /* printf("%2d %8.2f\n", i, j); */
927     }
928
929
930   auto_track_init (mi);
931 }
932
933
934 #ifdef DEBUG
935
936 static void
937 draw_grid (ModeInfo *mi)
938 {
939   if (!MI_IS_WIREFRAME(mi))
940     {
941       glDisable(GL_TEXTURE_2D);
942       glDisable(GL_BLEND);
943     }
944   glPushMatrix();
945
946   glColor3f(1, 1, 1);
947   glBegin(GL_LINES);
948   glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
949   glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
950   glEnd();
951   glBegin(GL_LINE_LOOP);
952   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
953   glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2, 0);
954   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2, 0);
955   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
956   glEnd();
957   glBegin(GL_LINE_LOOP);
958   glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
959   glVertex3f(-GRID_SIZE/2, GRID_SIZE/2,  GRID_DEPTH/2);
960   glVertex3f( GRID_SIZE/2, GRID_SIZE/2,  GRID_DEPTH/2);
961   glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
962   glEnd();
963   glBegin(GL_LINE_LOOP);
964   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
965   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   glEnd();
969   glBegin(GL_LINES);
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);
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);
978   glEnd();
979   glPopMatrix();
980   if (!MI_IS_WIREFRAME(mi))
981     {
982       glEnable(GL_TEXTURE_2D);
983       glEnable(GL_BLEND);
984     }
985 }
986 #endif /* DEBUG */
987
988
989 ENTRYPOINT void
990 draw_matrix (ModeInfo *mi)
991 {
992   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
993   Display *dpy = MI_DISPLAY(mi);
994   Window window = MI_WINDOW(mi);
995   int i;
996
997   if (!mp->glx_context)
998     return;
999
1000   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
1001
1002   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1003
1004   glPushMatrix ();
1005
1006   if (do_rotate)
1007     {
1008       glRotatef (mp->view_x, 1, 0, 0);
1009       glRotatef (mp->view_y, 0, 1, 0);
1010     }
1011
1012 #ifdef DEBUG
1013 # if 0
1014   glScalef(0.5, 0.5, 0.5);
1015 # endif
1016 # if 0
1017   glRotatef(-30, 0, 1, 0); 
1018 # endif
1019   draw_grid (mi);
1020 #endif
1021
1022   mi->polygon_count = 0;
1023
1024   /* Render (and tick) each strip, starting at the back
1025      (draw the ones farthest from the camera first, to make
1026      the alpha transparency work out right.)
1027    */
1028   {
1029     strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1030     for (i = 0; i < mp->nstrips; i++)
1031       sorted[i] = &mp->strips[i];
1032     qsort (sorted, i, sizeof(*sorted), cmp_strips);
1033
1034     for (i = 0; i < mp->nstrips; i++)
1035       {
1036         strip *s = sorted[i];
1037         tick_strip (mi, s);
1038         draw_strip (mi, s);
1039       }
1040     free (sorted);
1041   }
1042
1043   auto_track (mi);
1044
1045 #if 0
1046   glBegin(GL_QUADS);
1047   glColor3f(1,1,1);
1048   glTexCoord2f (0,0);  glVertex3f(-15,-15,0);
1049   glTexCoord2f (0,1);  glVertex3f(-15,15,0);
1050   glTexCoord2f (1,1);  glVertex3f(15,15,0);
1051   glTexCoord2f (1,0);  glVertex3f(15,-15,0);
1052   glEnd();
1053 #endif
1054
1055   glPopMatrix ();
1056
1057   if (mi->fps_p) do_fps (mi);
1058   glFinish();
1059
1060   glXSwapBuffers(dpy, window);
1061 }
1062
1063 XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)
1064
1065 #endif /* USE_GL */