home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #2 / Amiga Plus CD - 2004 - No. 02.iso / AmigaPlus / Tools / Anwendungen / glmatrix / source / glmatrix.c < prev    next >
Encoding:
C/C++ Source or Header  |  2004-01-31  |  28.6 KB  |  1,175 lines

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