1 /* glmatrix, Copyright (c) 2003 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * GLMatrix -- simulate the text scrolls from the movie "The Matrix".
13 * This program does a 3D rendering of the dropping characters that
14 * appeared in the title sequences of the movies. See also `xmatrix'
15 * for a simulation of what the computer monitors actually *in* the
19 #include <X11/Intrinsic.h>
21 extern XtAppContext app;
23 #define PROGCLASS "GLMatrix"
24 #define HACK_INIT init_matrix
25 #define HACK_DRAW draw_matrix
26 #define HACK_RESHAPE reshape_matrix
27 #define HACK_HANDLE_EVENT matrix_handle_event
28 #define EVENT_MASK PointerMotionMask
29 #define matrix_opts xlockmore_opts
31 #define DEF_SPEED "1.0"
32 #define DEF_DENSITY "20"
33 #define DEF_FOG "True"
34 #define DEF_WAVES "True"
35 #define DEF_ROTATE "True"
36 #define DEF_TEXTURE "True"
37 #define DEF_MODE "Matrix"
39 #define DEFAULTS "*delay: 30000 \n" \
40 "*showFPS: False \n" \
41 "*wireframe: False \n" \
42 "*mode: " DEF_MODE " \n" \
43 "*speed: " DEF_SPEED " \n" \
44 "*density: " DEF_DENSITY " \n" \
45 "*fog: " DEF_FOG " \n" \
46 "*waves: " DEF_WAVES " \n" \
47 "*texture: " DEF_TEXTURE " \n" \
48 "*rotate: " DEF_ROTATE " \n" \
51 #define countof(x) (sizeof((x))/sizeof((*x)))
54 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
56 #include "xlockmore.h"
57 #include "xpm-ximage.h"
60 #include "../images/matrix3.xpm"
62 #ifdef USE_GL /* whole file */
72 static int real_char_rows;
74 static int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
75 192, 193, 194, 195, 196, 197, 198, 199,
76 200, 201, 202, 203, 204, 205, 206, 207 };
77 static int decimal_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
78 static int hex_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
79 33, 34, 35, 36, 37, 38 };
80 static int binary_encoding[] = { 16, 17 };
81 static int dna_encoding[] = { 33, 35, 39, 52 };
83 static unsigned char char_map[256] = {
84 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0 */
85 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 16 */
86 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
87 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
88 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
89 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
90 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
91 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
92 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 128 */
93 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 144 */
94 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
95 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
96 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
97 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
98 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
99 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
103 #define CURSOR_GLYPH 97
107 #define GRID_SIZE 70 /* width and height of the arena */
108 #define GRID_DEPTH 35 /* depth of the arena */
109 #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */
110 #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */
112 static struct { GLfloat x, y; } nice_views[] = {
114 { 0, -20 }, /* this is a list of viewer rotations that look nice. */
115 { 0, 20 }, /* every now and then we switch to a new one. */
116 { 25, 0 }, /* (but we only use the first one at start-up.) */
125 { 0, 0 }, /* prefer these */
134 GLfloat x, y, z; /* position of strip */
135 GLfloat dx, dy, dz; /* velocity of strip */
137 Bool erasing_p; /* Whether this strip is on its way out. */
139 int spinner_glyph; /* the bottommost glyph -- the feeder */
140 GLfloat spinner_y; /* where on the strip the bottom glyph is */
141 GLfloat spinner_speed; /* how fast the bottom glyph drops */
143 int glyphs[GRID_SIZE]; /* the other glyphs on the strip, which will be
144 revealed by the dropping spinner.
145 0 means no glyph; negative means "spinner".
146 If non-zero, real value is abs(G)-1. */
148 int spin_speed; /* Rotate all spinners every this-many frames */
149 int spin_tick; /* frame counter */
151 int wave_position; /* Waves of brightness wash down the strip. */
152 int wave_speed; /* every this-many frames. */
153 int wave_tick; /* frame counter. */
159 GLXContext *glx_context;
166 GLfloat tex_char_width, tex_char_height;
168 /* auto-tracking direction of view */
169 int last_view, target_view;
170 GLfloat view_x, view_y;
171 int view_steps, view_tick;
172 Bool auto_tracking_p;
174 } matrix_configuration;
176 static matrix_configuration *mps = NULL;
178 static GLfloat brightness_ramp[WAVE_SIZE];
180 static GLfloat speed;
181 static GLfloat density;
183 static Bool do_waves;
184 static Bool do_rotate;
185 static Bool do_texture;
186 static char *mode_str;
188 static XrmOptionDescRec opts[] = {
189 { "-speed", ".speed", XrmoptionSepArg, 0 },
190 { "-density", ".density", XrmoptionSepArg, 0 },
191 { "-mode", ".mode", XrmoptionSepArg, 0 },
192 { "-binary", ".mode", XrmoptionNoArg, "binary" },
193 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" },
194 { "-decimal", ".mode", XrmoptionNoArg, "decimal" },
195 { "-dna", ".mode", XrmoptionNoArg, "dna" },
196 { "-fog", ".fog", XrmoptionNoArg, "True" },
197 { "+fog", ".fog", XrmoptionNoArg, "False" },
198 { "-waves", ".waves", XrmoptionNoArg, "True" },
199 { "+waves", ".waves", XrmoptionNoArg, "False" },
200 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
201 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
202 {"-texture", ".texture", XrmoptionNoArg, "True" },
203 {"+texture", ".texture", XrmoptionNoArg, "False" },
206 static argtype vars[] = {
207 {(caddr_t *) &mode_str, "mode", "Mode", DEF_MODE, t_String},
208 {(caddr_t *) &speed, "speed", "Speed", DEF_SPEED, t_Float},
209 {(caddr_t *) &density, "density", "Density", DEF_DENSITY, t_Float},
210 {(caddr_t *) &do_fog, "fog", "Fog", DEF_FOG, t_Bool},
211 {(caddr_t *) &do_waves, "waves", "Waves", DEF_WAVES, t_Bool},
212 {(caddr_t *) &do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
213 {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
216 ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
219 /* Re-randomize the state of one strip.
222 reset_strip (ModeInfo *mi, strip *s)
224 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
227 memset (s, 0, sizeof(*s));
228 s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
229 s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5)); /* shift top slightly */
230 s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
234 /* s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
236 s->dz = (BELLRAND(0.02) * speed);
238 s->spinner_speed = (BELLRAND(0.3) * speed);
240 s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
243 s->wave_position = 0;
244 s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
247 for (i = 0; i < GRID_SIZE; i++)
249 int draw_p = (random() % 7);
250 int spin_p = (draw_p && !(random() % 20));
252 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
257 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
261 /* Animate the strip one step. Reset if it has reached the bottom.
264 tick_strip (ModeInfo *mi, strip *s)
266 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
269 if (mp->button_down_p)
276 if (s->z > GRID_DEPTH * SPLASH_RATIO) /* splashed into screen */
282 s->spinner_y += s->spinner_speed;
283 if (s->spinner_y >= GRID_SIZE)
294 s->spinner_speed /= 2; /* erase it slower than we drew it */
298 /* Spin the spinners. */
300 if (s->spin_tick > s->spin_speed)
303 s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
304 for (i = 0; i < GRID_SIZE; i++)
305 if (s->glyphs[i] < 0)
307 s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
308 if (! (random() % 800)) /* sometimes they stop spinning */
309 s->glyphs[i] = -s->glyphs[i];
313 /* Move the color (brightness) wave. */
315 if (s->wave_tick > s->wave_speed)
319 if (s->wave_position >= WAVE_SIZE)
320 s->wave_position = 0;
325 /* Draw a single character at the given position and brightness.
328 draw_glyph (ModeInfo *mi, int glyph,
329 GLfloat x, GLfloat y, GLfloat z,
332 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
333 int wire = MI_IS_WIREFRAME(mi);
334 GLfloat w = mp->tex_char_width;
335 GLfloat h = mp->tex_char_height;
338 Bool spinner_p = (glyph < 0);
340 if (glyph == 0) abort();
341 if (glyph < 0) glyph = -glyph;
354 int ccx = ((glyph - 1) % CHAR_COLS);
355 int ccy = ((glyph - 1) / CHAR_COLS);
358 cy = (real_char_rows - ccy - 1) * h;
363 depth = (z / GRID_DEPTH) + 0.5; /* z ratio from back/front */
364 depth = 0.2 + (depth * 0.8); /* scale to range [0.2 - 1.0] */
365 brightness *= depth; /* so no row goes all black. */
370 GLfloat r, g, b, a = 1;
371 if (!do_texture && !spinner_p)
372 r = b = 0, g = brightness;
374 r = g = b = brightness;
376 /* If the glyph is very close to the screen (meaning it is very large,
377 and is about to splash into the screen and vanish) then start fading
378 it out, proportional to how close to the glass it is.
380 if (z > GRID_DEPTH/2)
382 GLfloat ratio = ((z - GRID_DEPTH/2) /
383 ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
384 int i = ratio * WAVE_SIZE;
387 else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
389 a = brightness_ramp[i];
391 /* I don't understand this -- if I change the alpha on the color of
392 the quad, I'd expect that to make the quad more transparent.
393 But instead, it seems to be making the transparent parts of the
394 texture on the quad be *less* transparent! So as we fade out,
395 we fade towards a completely solid rectangle. WTF?
397 So, for now, instead of changing the alpha, just make the colors
398 be darker. This isn't quite right (it causes a large dark glyph
399 to occlude the brighter glyphs behind it) but it's close...
411 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
412 glNormal3f (0, 0, 1);
413 glTexCoord2f (cx, cy); glVertex3f (x, y, z);
414 glTexCoord2f (cx+w, cy); glVertex3f (x+S, y, z);
415 glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
416 glTexCoord2f (cx, cy+h); glVertex3f (x, y+S, z);
419 if (wire && spinner_p)
422 glVertex3f (x, y, z);
423 glVertex3f (x+S, y+S, z);
424 glVertex3f (x, y+S, z);
425 glVertex3f (x+S, y, z);
433 /* Draw all the visible glyphs in the strip.
436 draw_strip (ModeInfo *mi, strip *s)
439 for (i = 0; i < GRID_SIZE; i++)
441 int g = s->glyphs[i];
442 Bool below_p = (s->spinner_y >= i);
447 if (g && below_p) /* don't draw cells below the spinner */
454 int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
456 brightness = brightness_ramp[j];
459 draw_glyph (mi, g, s->x, s->y - i, s->z, brightness);
464 draw_glyph (mi, s->spinner_glyph, s->x, s->y - s->spinner_y, s->z, 1.0);
468 /* qsort comparator for sorting strips by z position */
470 cmp_strips (const void *aa, const void *bb)
472 const strip *a = *(strip **) aa;
473 const strip *b = *(strip **) bb;
474 return ((int) (a->z * 10000) -
475 (int) (b->z * 10000));
483 auto_track_init (ModeInfo *mi)
485 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
488 mp->view_x = nice_views[mp->last_view].x;
489 mp->view_y = nice_views[mp->last_view].y;
490 mp->view_steps = 100;
492 mp->auto_tracking_p = False;
497 auto_track (ModeInfo *mi)
499 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
503 if (mp->button_down_p)
506 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
507 if (! mp->auto_tracking_p)
510 if (++tick < 20/speed) return;
512 if (! (random() % 20))
513 mp->auto_tracking_p = True;
520 GLfloat ox = nice_views[mp->last_view].x;
521 GLfloat oy = nice_views[mp->last_view].y;
522 GLfloat tx = nice_views[mp->target_view].x;
523 GLfloat ty = nice_views[mp->target_view].y;
525 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
527 GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
529 mp->view_x = (ox + ((tx - ox) * th));
530 mp->view_y = (oy + ((ty - oy) * th));
533 if (mp->view_tick >= mp->view_steps)
536 mp->view_steps = (350.0 / speed);
537 mp->last_view = mp->target_view;
538 mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
539 mp->auto_tracking_p = False;
545 /* Window management, etc
548 reshape_matrix (ModeInfo *mi, int width, int height)
550 GLfloat h = (GLfloat) height / (GLfloat) width;
552 glViewport (0, 0, (GLint) width, (GLint) height);
554 glMatrixMode(GL_PROJECTION);
556 gluPerspective (80.0, 1/h, 1.0, 100);
558 glMatrixMode(GL_MODELVIEW);
560 gluLookAt( 0.0, 0.0, 25.0,
564 glClear(GL_COLOR_BUFFER_BIT);
569 matrix_handle_event (ModeInfo *mi, XEvent *event)
571 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
573 if (event->xany.type == ButtonPress &&
574 event->xbutton.button & Button1)
576 mp->button_down_p = True;
579 else if (event->xany.type == ButtonRelease &&
580 event->xbutton.button & Button1)
582 mp->button_down_p = False;
593 union { int i; char c[sizeof(int)]; } u;
599 /* The image with the characters in it is 512x598, meaning that it needs to
600 be copied into a 512x1024 texture. But some machines can't handle textures
601 that large... And it turns out that we aren't using most of the characters
602 in that image anyway, since this program doesn't do anything that makes use
603 of the full range of Latin1 characters. So... this function tosses out the
604 last 32 of the Latin1 characters, resulting in a 512x506 image, which we
605 can then stuff in a 512x512 texture. Voila.
607 If this hack ever grows into something that displays full Latin1 text,
608 well then, Something Else Will Need To Be Done.
611 spank_image (XImage *xi)
613 int ch = xi->height / CHAR_ROWS;
615 unsigned char *bits = (unsigned char *) xi->data;
616 unsigned char *from, *to, *s, *end;
617 int L = xi->bytes_per_line * ch;
620 /* Copy row 12 into 10 (which really means, copy 2 into 0,
621 since texture data is upside down.).
623 to = bits + (L * cut);
630 /* Then, pull all the bits down by 2 rows.
633 from = bits + (L * cut);
634 end = bits + (L * CHAR_ROWS);
639 /* And clear out the rest, for good measure.
641 from = bits + (L * (CHAR_ROWS - cut));
642 end = bits + (L * CHAR_ROWS);
647 xi->height -= (cut * ch);
648 real_char_rows -= cut;
650 /* Finally, pull the map indexes back to match the new bits.
652 for (i = 0; i < countof(matrix_encoding); i++)
653 if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
654 matrix_encoding[i] -= (cut * CHAR_COLS);
659 load_textures (ModeInfo *mi, Bool flip_p)
661 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
667 /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
668 So we waste some padding rows to round up.
670 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
674 real_char_rows = CHAR_ROWS;
677 if (xi->height != 512 && xi->height != 1024)
679 xi->height = (xi->height < 512 ? 512 : 1024);
680 xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
683 fprintf(stderr, "%s: out of memory\n", progname);
688 if (xi->width != 512) abort();
689 if (xi->height != 512 && xi->height != 1024) abort();
691 /* char size in pixels */
692 cw = orig_w / CHAR_COLS;
693 ch = orig_h / CHAR_ROWS;
695 /* char size in ratio of final (padded) texture size */
696 mp->tex_char_width = (GLfloat) cw / xi->width;
697 mp->tex_char_height = (GLfloat) ch / xi->height;
699 /* Flip each character's bits horizontally -- we could also just do this
700 by reversing the texture coordinates on the quads, but on some systems
701 that slows things down a lot.
706 unsigned long buf[100];
707 for (y = 0; y < xi->height; y++)
708 for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
710 for (x = 0; x < cw; x++)
711 buf[x] = XGetPixel (xi, xx+x, y);
712 for (x = 0; x < cw; x++)
713 XPutPixel (xi, xx+x, y, buf[cw-x-1]);
717 /* The pixmap is a color image with no transparency. Set the texture's
718 alpha to be the green channel, and set the green channel to be 100%.
721 int rpos, gpos, bpos, apos; /* bitfield positions */
723 rpos = 24, gpos = 16, bpos = 8, apos = 0;
725 rpos = 0, gpos = 8, bpos = 16, apos = 24;
727 for (y = 0; y < xi->height; y++)
728 for (x = 0; x < xi->width; x++)
730 unsigned long p = XGetPixel (xi, x, y);
731 unsigned char r = (p >> rpos) & 0xFF;
732 unsigned char g = (p >> gpos) & 0xFF;
733 unsigned char b = (p >> bpos) & 0xFF;
734 unsigned char a = ~g;
736 p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
737 XPutPixel (xi, x, y, p);
741 /* Now load the texture into GL.
744 glGenTextures (1, &mp->texture);
746 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
747 glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
748 glBindTexture (GL_TEXTURE_2D, mp->texture);
749 check_gl_error ("texture init");
750 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
751 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
754 sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
755 check_gl_error (buf);
758 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
759 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
761 /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
762 faint solid green border around the texture if it is *not* REPEAT!
764 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
765 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
767 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
768 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
769 check_gl_error ("texture param");
771 xi->data = 0; /* don't free the texture data */
777 init_matrix (ModeInfo *mi)
779 matrix_configuration *mp;
780 int wire = MI_IS_WIREFRAME(mi);
788 mps = (matrix_configuration *)
789 calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
791 fprintf(stderr, "%s: out of memory\n", progname);
796 mp = &mps[MI_SCREEN(mi)];
797 mp->glx_context = init_GL(mi);
799 if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
802 mp->glyph_map = matrix_encoding;
803 mp->nglyphs = countof(matrix_encoding);
805 else if (!strcasecmp (mode_str, "dna"))
808 mp->glyph_map = dna_encoding;
809 mp->nglyphs = countof(dna_encoding);
811 else if (!strcasecmp (mode_str, "bin") ||
812 !strcasecmp (mode_str, "binary"))
815 mp->glyph_map = binary_encoding;
816 mp->nglyphs = countof(binary_encoding);
818 else if (!strcasecmp (mode_str, "hex") ||
819 !strcasecmp (mode_str, "hexadecimal"))
822 mp->glyph_map = hex_encoding;
823 mp->nglyphs = countof(hex_encoding);
825 else if (!strcasecmp (mode_str, "dec") ||
826 !strcasecmp (mode_str, "decimal"))
829 mp->glyph_map = decimal_encoding;
830 mp->nglyphs = countof(decimal_encoding);
835 "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
840 reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
842 glShadeModel(GL_SMOOTH);
844 glDisable(GL_DEPTH_TEST);
845 glDisable(GL_CULL_FACE);
846 glEnable(GL_NORMALIZE);
850 load_textures (mi, flip_p);
851 glEnable(GL_TEXTURE_2D);
853 glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
856 /* to scale coverage-percent to strips, this number looks about right... */
857 mp->nstrips = (int) (density * 2.2);
858 if (mp->nstrips < 1) mp->nstrips = 1;
859 else if (mp->nstrips > 2000) mp->nstrips = 2000;
862 mp->strips = calloc (mp->nstrips, sizeof(strip));
863 for (i = 0; i < mp->nstrips; i++)
865 strip *s = &mp->strips[i];
868 /* If we start all strips from zero at once, then the first few seconds
869 of the animation are much denser than normal. So instead, set all
870 the initial strips to erase-mode with random starting positions.
871 As these die off at random speeds and are re-created, we'll get a
872 more consistent density. */
874 s->spinner_y = frand(GRID_SIZE);
875 memset (s->glyphs, 0, sizeof(s->glyphs)); /* no visible glyphs */
878 /* Compute the brightness ramp.
880 for (i = 0; i < WAVE_SIZE; i++)
882 GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
883 j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */
884 j = sin (j); /* j ranges from 0.0 - 1.0 */
885 j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */
886 brightness_ramp[i] = j;
887 /* printf("%2d %8.2f\n", i, j); */
891 auto_track_init (mi);
898 draw_grid (ModeInfo *mi)
900 if (!MI_IS_WIREFRAME(mi))
902 glDisable(GL_TEXTURE_2D);
908 glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
909 glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
911 glBegin(GL_LINE_LOOP);
912 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
913 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
914 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
915 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
917 glBegin(GL_LINE_LOOP);
918 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
919 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
920 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
921 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
923 glBegin(GL_LINE_LOOP);
924 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
925 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
926 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
927 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
930 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
931 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
932 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
933 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
934 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
935 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
936 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
937 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
940 if (!MI_IS_WIREFRAME(mi))
942 glEnable(GL_TEXTURE_2D);
950 draw_matrix (ModeInfo *mi)
952 matrix_configuration *mp = &mps[MI_SCREEN(mi)];
953 Display *dpy = MI_DISPLAY(mi);
954 Window window = MI_WINDOW(mi);
957 if (!mp->glx_context)
960 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
966 glRotatef (mp->view_x, 1, 0, 0);
967 glRotatef (mp->view_y, 0, 1, 0);
972 glScalef(0.5, 0.5, 0.5);
975 glRotatef(-30, 0, 1, 0);
980 mi->polygon_count = 0;
982 /* Render (and tick) each strip, starting at the back
983 (draw the ones farthest from the camera first, to make
984 the alpha transparency work out right.)
987 strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
988 for (i = 0; i < mp->nstrips; i++)
989 sorted[i] = &mp->strips[i];
990 qsort (sorted, i, sizeof(*sorted), cmp_strips);
992 for (i = 0; i < mp->nstrips; i++)
994 strip *s = sorted[i];
1006 glTexCoord2f (0,0); glVertex3f(-15,-15,0);
1007 glTexCoord2f (0,1); glVertex3f(-15,15,0);
1008 glTexCoord2f (1,1); glVertex3f(15,15,0);
1009 glTexCoord2f (1,0); glVertex3f(15,-15,0);
1015 if (mi->fps_p) do_fps (mi);
1018 glXSwapBuffers(dpy, window);