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