96ddaccd5e3c610e60f95e27c1c931b204462fc8
[xscreensaver] / hacks / glx / menger.c
1 /* menger, Copyright (c) 2001 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  * Generates a 3D Menger Sponge gasket:
12  *
13  *                                ___+._______
14  *                           __-""   --     __"""----._____
15  *                    __.--"" -- ___--+---_____.     __  .+'|
16  *              _.-'""  __    +:"__   | ._..+"" __    .+'   F
17  *            J"--.____         __ """""+"         .+'  .J  F
18  *            J        """""---.___       --   .+'"     F'  F
19  *             L                   """""--...+'    .J       F
20  *             L   F"9      --.            |   .   F'      J
21  *             L   -_J      L_J      F"9   | ;'J    .+J .J J
22  *             |                     L_J   | F.'  .'| J F' J
23  *             |        |"""--.__          | '   |""  J    J
24  *             J   ._   J ;;; |  "L        |   . |-___J    |
25  *             J   L J  J ;-' |   L        | .'J |_  .'  . |
26  *             J    ""  J    .---_L  F"9   | F.' | .'   FJ |
27  *              L       J .-'  __ |  L_J   | '   :'     ' .+
28  *              L       '--.___   |        |       .J   .'
29  *              |  F"9         """'        |   .   F' .'
30  *              |  -_J      F"9            | .'J    .'
31  *              +__         -_J      F"9   | F.'  .'
32  *                 """--___          L_J   | '  .'
33  *                         """---___       |  .'
34  *                                  ""---._|.'
35  *
36  *  The straightforward way to generate this object creates way more polygons
37  *  than are needed, since there end up being many buried, interior faces.
38  *  So first we go through and generate the list of all the squares; then we
39  *  sort the list and delete any squares that have a duplicate: if there are
40  *  two of them, then they will be interior and facing each other, so we
41  *  don't need either.  Doing this reduces the polygon count by 20% - 33%.
42  *
43  *  Another optimization we could do to reduce the polygon count would be to
44  *  merge adjascent coplanar squares together into rectangles.  This would
45  *  result in the outer faces being composed of 1xN strips.  It's tricky to
46  *  to find these adjascent faces in non-exponential time, though.
47  *
48  *  We could take advantage of the object's triple symmetry to reduce the
49  *  size of the `squares' array, though that wouldn't actually reduce the
50  *  number of polygons in the scene.
51  *
52  *  We could actually simulate large depths with a texture map -- if the
53  *  depth is such that the smallest holes are only a few pixels across,
54  *  just draw them as spots on the surface!  It would look the same.
55  */
56
57 #include <X11/Intrinsic.h>
58
59 extern XtAppContext app;
60
61 #define PROGCLASS       "Menger"
62 #define HACK_INIT       init_sponge
63 #define HACK_DRAW       draw_sponge
64 #define HACK_RESHAPE    reshape_sponge
65 #define sws_opts        xlockmore_opts
66
67 #define DEF_SPIN        "True"
68 #define DEF_WANDER      "True"
69 #define DEF_SPEED       "150"
70 #define DEF_MAX_DEPTH   "3"
71 #define DEF_OPTIMIZE    "True"
72
73 #define DEFAULTS        "*delay:         30000          \n" \
74                         "*showFPS:       False          \n" \
75                         "*wireframe:     False          \n" \
76                         "*maxDepth:    " DEF_MAX_DEPTH "\n" \
77                         "*speed:"        DEF_SPEED     "\n" \
78                         "*optimize:"     DEF_OPTIMIZE  "\n" \
79                         "*spin:        " DEF_SPIN      "\n" \
80                         "*wander:      " DEF_WANDER    "\n" \
81
82
83 #undef countof
84 #define countof(x) (sizeof((x))/sizeof((*x)))
85
86 #include "xlockmore.h"
87 #include "colors.h"
88 #include <ctype.h>
89
90 #ifdef USE_GL /* whole file */
91
92 #include <GL/glu.h>
93
94 typedef struct {
95   unsigned int x0 : 8;     /* 8     bottom left */
96   unsigned int y0 : 8;     /* 16                */
97   unsigned int z0 : 8;     /* 24                */
98
99   unsigned int x1 : 8;     /* 32    top right   */
100   unsigned int y1 : 8;     /* 40                */
101   unsigned int z1 : 8;     /* 48                */
102   
103   int          nx : 2;     /* 50    normal      */
104   int          ny : 2;     /* 52                */
105   int          nz : 2;     /* 54                */
106
107                            /* 2 bits left over; 56 bits / 7 bytes total, */
108                            /* which is surely rounded up to 8 bytes.     */
109 } square;
110
111
112 typedef struct {
113   GLXContext *glx_context;
114
115   GLfloat rotx, roty, rotz;        /* current object rotation */
116   GLfloat dx, dy, dz;              /* current rotational velocity */
117   GLfloat ddx, ddy, ddz;           /* current rotational acceleration */
118   GLfloat d_max;                   /* max velocity */
119
120   GLuint sponge_list0;            /* we store X, Y, and Z-facing surfaces */
121   GLuint sponge_list1;            /* in their own lists, to make it easy  */
122   GLuint sponge_list2;            /* to color them differently.           */
123
124   square *squares;
125   unsigned long squares_size;
126   unsigned long squares_fp;
127
128   int current_depth;
129
130   int ncolors;
131   XColor *colors;
132   int ccolor0;
133   int ccolor1;
134   int ccolor2;
135
136 } sponge_configuration;
137
138 static sponge_configuration *sps = NULL;
139
140 static char *do_spin;
141 static Bool do_wander;
142 static int speed;
143 static Bool do_optimize;
144 static int max_depth;
145
146 static XrmOptionDescRec opts[] = {
147   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
148   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
149   { "-wander", ".wander", XrmoptionNoArg, "True" },
150   { "+wander", ".wander", XrmoptionNoArg, "False" },
151   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
152   { "-optimize", ".optimize", XrmoptionNoArg, "True" },
153   { "+optimize", ".optimize", XrmoptionNoArg, "False" },
154   {"-depth",   ".maxDepth", XrmoptionSepArg, (caddr_t) 0 },
155 };
156
157 static argtype vars[] = {
158   {(caddr_t *) &do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
159   {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
160   {(caddr_t *) &speed,     "speed",  "Speed",  DEF_SPEED,  t_Int},
161   {(caddr_t *) &do_optimize, "optimize", "Optimize", DEF_OPTIMIZE, t_Bool},
162   {(caddr_t *) &max_depth, "maxDepth", "MaxDepth", DEF_MAX_DEPTH, t_Int},
163 };
164
165 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
166
167
168 /* Window management, etc
169  */
170 void
171 reshape_sponge (ModeInfo *mi, int width, int height)
172 {
173   GLfloat h = (GLfloat) height / (GLfloat) width;
174
175   glViewport (0, 0, (GLint) width, (GLint) height);
176
177   glMatrixMode(GL_PROJECTION);
178   glLoadIdentity();
179
180   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
181   gluLookAt( 0.0, 0.0, 15.0,
182              0.0, 0.0, 0.0,
183              0.0, 1.0, 0.0);
184   glMatrixMode(GL_MODELVIEW);
185   glLoadIdentity();
186   glTranslatef(0.0, 0.0, -15.0);
187
188   glClear(GL_COLOR_BUFFER_BIT);
189 }
190
191
192 static void
193 gl_init (ModeInfo *mi)
194 {
195 /*  sponge_configuration *sp = &sps[MI_SCREEN(mi)]; */
196   int wire = MI_IS_WIREFRAME(mi);
197
198   static GLfloat pos[4] = {-4.0, 3.0, 10.0, 1.0};
199
200   if (!wire)
201     {
202       glLightfv(GL_LIGHT0, GL_POSITION, pos);
203       glEnable(GL_CULL_FACE);
204       glEnable(GL_LIGHTING);
205       glEnable(GL_LIGHT0);
206       glEnable(GL_DEPTH_TEST);
207     }
208 }
209
210
211 /* lifted from lament.c */
212 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
213 #define RANDSIGN() ((random() & 1) ? 1 : -1)
214
215 static void
216 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
217 {
218   double ppos = *pos;
219
220   /* tick position */
221   if (ppos < 0)
222     ppos = -(ppos + *v);
223   else
224     ppos += *v;
225
226   if (ppos > 1.0)
227     ppos -= 1.0;
228   else if (ppos < 0)
229     ppos += 1.0;
230
231   if (ppos < 0) abort();
232   if (ppos > 1.0) abort();
233   *pos = (*pos > 0 ? ppos : -ppos);
234
235   /* accelerate */
236   *v += *dv;
237
238   /* clamp velocity */
239   if (*v > max_v || *v < -max_v)
240     {
241       *dv = -*dv;
242     }
243   /* If it stops, start it going in the other direction. */
244   else if (*v < 0)
245     {
246       if (random() % 4)
247         {
248           *v = 0;
249
250           /* keep going in the same direction */
251           if (random() % 2)
252             *dv = 0;
253           else if (*dv < 0)
254             *dv = -*dv;
255         }
256       else
257         {
258           /* reverse gears */
259           *v = -*v;
260           *dv = -*dv;
261           *pos = -*pos;
262         }
263     }
264
265   /* Alter direction of rotational acceleration randomly. */
266   if (! (random() % 120))
267     *dv = -*dv;
268
269   /* Change acceleration very occasionally. */
270   if (! (random() % 200))
271     {
272       if (*dv == 0)
273         *dv = 0.00001;
274       else if (random() & 1)
275         *dv *= 1.2;
276       else
277         *dv *= 0.8;
278     }
279 }
280
281
282 /* Pushes a 1x1x1 cube at XYZ into the sp->squares list.
283  */
284 static void
285 cube (sponge_configuration *sp, Bool wireframe_p,
286       int x, int y, int z, int s)
287 {
288   square *sq;
289
290 # define PP(NX, NY, NZ, X0, Y0, Z0, X1, Y1, Z1) \
291          (sq = &sp->squares[sp->squares_fp++], \
292           sq->nx = (NX), \
293           sq->ny = (NY), \
294           sq->nz = (NZ), \
295           sq->x0 = x+((X0)*s), \
296           sq->y0 = y+((Y0)*s), \
297           sq->z0 = z+((Z0)*s), \
298           sq->x1 = x+((X1)*s), \
299           sq->y1 = y+((Y1)*s), \
300           sq->z1 = z+((Z1)*s))
301
302   PP (0,  0, -1,   0, 1, 0,   1, 0, 0);
303   PP (0,  0,  1,   0, 0, 1,   1, 1, 1);
304   PP (0, -1,  0,   0, 0, 0,   1, 0, 1);
305   PP (0,  1,  0,   0, 1, 1,   1, 1, 0);
306
307   if (wireframe_p) return;  /* don't need the rest */
308
309   PP (-1, 0,  0,   0, 0, 1,   0, 1, 0);
310   PP ( 1, 0,  0,   1, 0, 0,   1, 1, 1);
311 # undef PP
312
313   if (sp->squares_fp >= sp->squares_size) abort();
314 }
315
316
317 static int
318 iexp (int i, int n)
319 {
320   int ii = 1;
321   while (n-- > 0) ii *= i;
322   return ii;
323 }
324
325
326 static void
327 descend_cubes (sponge_configuration *sp, Bool wireframe_p, int countdown,
328                int x, int y, int z)
329 {
330   int descend_p = (countdown > 0);
331   int facet_size = iexp (3, countdown);
332   int s = facet_size;
333
334   if (wireframe_p)
335     {
336       cube (sp, wireframe_p, x+1*s, y+1*s, z,     1);
337       cube (sp, wireframe_p, x+1*s, y+1*s, z+2*s, 1);
338       cube (sp, wireframe_p, x,     y+1*s, z+1*s, 1);
339       cube (sp, wireframe_p, x+2*s, y+1*s, z+1*s, 1);
340       cube (sp, wireframe_p, x+1*s, y,     z+1*s, 1);
341       cube (sp, wireframe_p, x+1*s, y+2*s, z+1*s, 1);
342
343       if (!descend_p) return;
344     }
345
346 # define CUBE(xx,yy,zz) \
347          (descend_p \
348           ? descend_cubes (sp, wireframe_p, countdown-1, \
349                            x+(xx)*s, y+(yy)*s, z+(zz)*s) \
350           : cube (sp, wireframe_p, x+(xx)*s, y+(yy)*s, z+(zz)*s, 1))
351
352   CUBE(0, 2, 0); CUBE(1, 2, 0); CUBE(2, 2, 0);   /* top front row */
353   CUBE(0, 1, 0);                CUBE(2, 1, 0);   /* middle front row */
354   CUBE(0, 0, 0); CUBE(1, 0, 0); CUBE(2, 0, 0);   /* bottom front row */
355   CUBE(0, 2, 1);                CUBE(2, 2, 1);   /* top middle row */
356   CUBE(0, 0, 1);                CUBE(2, 0, 1);   /* bottom middle row */
357   CUBE(0, 2, 2); CUBE(1, 2, 2); CUBE(2, 2, 2);   /* top back row */
358   CUBE(0, 1, 2);                CUBE(2, 1, 2);   /* middle back row */
359   CUBE(0, 0, 2); CUBE(1, 0, 2); CUBE(2, 0, 2);   /* bottom back row */
360 # undef CUBE
361 }
362
363
364 static int cmp_squares (const void *a, const void *b);
365 static void delete_redundant_faces (sponge_configuration *);
366
367 static void
368 build_sponge (sponge_configuration *sp, Bool wireframe_p, int countdown)
369 {
370   sp->squares_fp = 0;
371
372   if (countdown <= 0)
373     {
374       cube (sp, wireframe_p, 0, 0, 0, 1);
375     }
376   else
377     {
378       if (wireframe_p)
379         {
380           int facet_size = iexp(3, countdown);
381           cube (sp, wireframe_p, 0, 0, 0, facet_size);
382         }
383       descend_cubes (sp, wireframe_p, countdown - 1,
384                      0, 0, 0);
385     }
386
387   if (!wireframe_p && do_optimize)
388     {
389       qsort (sp->squares, sp->squares_fp, sizeof(*sp->squares),
390              cmp_squares);
391       delete_redundant_faces (sp);
392     }
393
394   {
395     int i, j;
396     square *sq;
397     GLfloat s = 1.0 / iexp (3, countdown);
398     s *= 3;
399
400     glDeleteLists (sp->sponge_list0, 1);
401     glDeleteLists (sp->sponge_list1, 1);
402     glDeleteLists (sp->sponge_list2, 1);
403
404     for (j = 0; j < 3; j++)
405       {
406         sq = sp->squares;
407         glNewList ((j == 0 ? sp->sponge_list0 :
408                     j == 1 ? sp->sponge_list1 :
409                     sp->sponge_list2),
410                    GL_COMPILE);
411         glPushMatrix();
412         glTranslatef (-1.5, -1.5, -1.5);
413         glScalef(s, s, s);
414
415         for (i = 0; i < sp->squares_fp; i++)
416           {
417             if ((j == 0 && sq->nx != 0) ||
418                 (j == 1 && sq->ny != 0) ||
419                 (j == 2 && sq->nz != 0))
420               {
421                 glBegin (wireframe_p ? GL_LINE_LOOP : GL_QUADS);
422                 glNormal3i (sq->nx, sq->ny, sq->nz);
423                 if (sq->nz)
424                   {
425                     glVertex3i (sq->x1, sq->y0, sq->z0);
426                     glVertex3i (sq->x1, sq->y1, sq->z0);
427                     glVertex3i (sq->x0, sq->y1, sq->z0);
428                     glVertex3i (sq->x0, sq->y0, sq->z0);
429                   }
430                 else if (sq->ny)
431                   {
432                     glVertex3i (sq->x1, sq->y0, sq->z0);
433                     glVertex3i (sq->x1, sq->y0, sq->z1);
434                     glVertex3i (sq->x0, sq->y0, sq->z1);
435                     glVertex3i (sq->x0, sq->y0, sq->z0);
436                   }
437                 else
438                   {
439                     glVertex3i (sq->x0, sq->y1, sq->z0);
440                     glVertex3i (sq->x0, sq->y1, sq->z1);
441                     glVertex3i (sq->x0, sq->y0, sq->z1);
442                     glVertex3i (sq->x0, sq->y0, sq->z0);
443                   }
444                 glEnd();
445               }
446             sq++;
447           }
448         glPopMatrix();
449         glEndList();
450       }
451   }
452 }
453
454
455 static int
456 cmp_squares (const void *aa, const void *bb)
457 {
458   square *a = (square *) aa;
459   square *b = (square *) bb;
460   int i, p0, p1;
461
462   p0 = (a->x0 < a->x1 ? a->x0 : a->x1);
463   p1 = (b->x0 < b->x1 ? b->x0 : b->x1);
464   if ((i = (p0 - p1))) return i;
465
466   p0 = (a->y0 < a->y1 ? a->y0 : a->y1);
467   p1 = (b->y0 < b->y1 ? b->y0 : b->y1);
468   if ((i = (p0 - p1))) return i;
469
470   p0 = (a->z0 < a->z1 ? a->z0 : a->z1);
471   p1 = (b->z0 < b->z1 ? b->z0 : b->z1);
472   if ((i = (p0 - p1))) return i;
473
474
475   p0 = (a->x0 > a->x1 ? a->x0 : a->x1);
476   p1 = (b->x0 > b->x1 ? b->x0 : b->x1);
477   if ((i = (p0 - p1))) return i;
478
479   p0 = (a->y0 > a->y1 ? a->y0 : a->y1);
480   p1 = (b->y0 > b->y1 ? b->y0 : b->y1);
481   if ((i = (p0 - p1))) return i;
482
483   p0 = (a->z0 > a->z1 ? a->z0 : a->z1);
484   p1 = (b->z0 > b->z1 ? b->z0 : b->z1);
485   if ((i = (p0 - p1))) return i;
486
487   return 0;
488
489 }
490
491
492 static void
493 delete_redundant_faces (sponge_configuration *sp)
494 {
495   square *sq = sp->squares;
496   square *end = sq + sp->squares_fp;
497   square *out = sq;
498   int i = 0;
499
500   while (sq < end)
501     {
502       if (cmp_squares (sq, sq+1)) /* they differ - keep this one */
503         {
504           if (sq != out)
505             *out = *sq;
506           out++;
507           i++;
508         }
509       sq++;
510     }
511
512 # if 0
513   fprintf (stderr, "%s: optimized away %d polygons (%d%%)\n",
514            progname,
515            sp->squares_fp - i,
516            100 - ((i * 100) / sp->squares_fp));
517 # endif
518
519   sp->squares_fp = i;
520 }
521
522
523 void 
524 init_sponge (ModeInfo *mi)
525 {
526   sponge_configuration *sp;
527
528   if (!sps) {
529     sps = (sponge_configuration *)
530       calloc (MI_NUM_SCREENS(mi), sizeof (sponge_configuration));
531     if (!sps) {
532       fprintf(stderr, "%s: out of memory\n", progname);
533       exit(1);
534     }
535
536     sp = &sps[MI_SCREEN(mi)];
537   }
538
539   sp = &sps[MI_SCREEN(mi)];
540
541   if ((sp->glx_context = init_GL(mi)) != NULL) {
542     gl_init(mi);
543     reshape_sponge (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
544   }
545
546   sp->rotx = frand(1.0) * RANDSIGN();
547   sp->roty = frand(1.0) * RANDSIGN();
548   sp->rotz = frand(1.0) * RANDSIGN();
549
550   /* bell curve from 0-3 degrees, avg 1.5 */
551   sp->dx = (frand(1) + frand(1) + frand(1)) / (360/2);
552   sp->dy = (frand(1) + frand(1) + frand(1)) / (360/2);
553   sp->dz = (frand(1) + frand(1) + frand(1)) / (360/2);
554
555   sp->d_max = sp->dx * 2;
556
557   sp->ddx = 0.00006 + frand(0.00003);
558   sp->ddy = 0.00006 + frand(0.00003);
559   sp->ddz = 0.00006 + frand(0.00003);
560
561   sp->ncolors = 128;
562   sp->colors = (XColor *) calloc(sp->ncolors, sizeof(XColor));
563   make_smooth_colormap (0, 0, 0,
564                         sp->colors, &sp->ncolors,
565                         False, 0, False);
566   sp->ccolor0 = 0;
567   sp->ccolor1 = sp->ncolors / 3;
568   sp->ccolor2 = sp->ccolor1 * 2;
569
570   {
571     int d = max_depth < 1 ? 1 : max_depth;
572     unsigned long facet_size = iexp(3, d);
573     unsigned long bytes;
574     float be_miserly = 0.741;
575     sp->squares_size = iexp(facet_size, 3) * 6 * be_miserly;
576     bytes = sp->squares_size * sizeof (*sp->squares);
577
578     sp->squares_fp = 0;
579     sp->squares = calloc (1, bytes);
580
581     if (!sp->squares)
582       {
583         fprintf (stderr, "%s: out of memory", progname);
584         if ((sp->squares_size >> 20) > 1)
585           fprintf (stderr, " (%luMB for %luM polygons)\n",
586                    bytes >> 20, sp->squares_size >> 20);
587         else
588           fprintf (stderr, " (%luKB for %luK polygons)\n",
589                    bytes >> 10, sp->squares_size >> 10);
590         exit (1);
591       }
592   }
593
594   sp->sponge_list0 = glGenLists (1);
595   sp->sponge_list1 = glGenLists (1);
596   sp->sponge_list2 = glGenLists (1);
597 }
598
599
600 void
601 draw_sponge (ModeInfo *mi)
602 {
603   sponge_configuration *sp = &sps[MI_SCREEN(mi)];
604   Display *dpy = MI_DISPLAY(mi);
605   Window window = MI_WINDOW(mi);
606
607   static GLfloat color0[4] = {0.0, 0.0, 0.0, 1.0};
608   static GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
609   static GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
610
611   static int tick = 99999;
612
613   if (!sp->glx_context)
614     return;
615
616   glShadeModel(GL_SMOOTH);
617
618   glEnable(GL_DEPTH_TEST);
619   glEnable(GL_NORMALIZE);
620   glEnable(GL_CULL_FACE);
621
622   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
623
624   glPushMatrix ();
625
626   glScalef(1.1, 1.1, 1.1);
627
628   {
629     GLfloat x, y, z;
630
631     if (do_wander)
632       {
633         static int frame = 0;
634
635 #       define SINOID(SCALE,SIZE) \
636         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
637
638         x = SINOID(0.0071, 8.0);
639         y = SINOID(0.0053, 6.0);
640         z = SINOID(0.0037, 15.0);
641         frame++;
642         glTranslatef(x, y, z);
643       }
644
645     if (do_spin)
646       {
647         x = sp->rotx;
648         y = sp->roty;
649         z = sp->rotz;
650         if (x < 0) x = 1 - (x + 1);
651         if (y < 0) y = 1 - (y + 1);
652         if (z < 0) z = 1 - (z + 1);
653
654         glRotatef(x * 360, 1.0, 0.0, 0.0);
655         glRotatef(y * 360, 0.0, 1.0, 0.0);
656         glRotatef(z * 360, 0.0, 0.0, 1.0);
657
658         rotate(&sp->rotx, &sp->dx, &sp->ddx, sp->d_max);
659         rotate(&sp->roty, &sp->dy, &sp->ddy, sp->d_max);
660         rotate(&sp->rotz, &sp->dz, &sp->ddz, sp->d_max);
661       }
662   }
663
664   color0[0] = sp->colors[sp->ccolor0].red   / 65536.0;
665   color0[1] = sp->colors[sp->ccolor0].green / 65536.0;
666   color0[2] = sp->colors[sp->ccolor0].blue  / 65536.0;
667
668   color1[0] = sp->colors[sp->ccolor1].red   / 65536.0;
669   color1[1] = sp->colors[sp->ccolor1].green / 65536.0;
670   color1[2] = sp->colors[sp->ccolor1].blue  / 65536.0;
671
672   color2[0] = sp->colors[sp->ccolor2].red   / 65536.0;
673   color2[1] = sp->colors[sp->ccolor2].green / 65536.0;
674   color2[2] = sp->colors[sp->ccolor2].blue  / 65536.0;
675
676
677   sp->ccolor0++;
678   sp->ccolor1++;
679   sp->ccolor2++;
680   if (sp->ccolor0 >= sp->ncolors) sp->ccolor0 = 0;
681   if (sp->ccolor1 >= sp->ncolors) sp->ccolor1 = 0;
682   if (sp->ccolor2 >= sp->ncolors) sp->ccolor2 = 0;
683
684   if (tick++ >= speed)
685     {
686       tick = 0;
687       if (sp->current_depth >= max_depth)
688         sp->current_depth = -max_depth;
689       sp->current_depth++;
690       build_sponge (sp,
691                     MI_IS_WIREFRAME(mi),
692                     (sp->current_depth < 0
693                      ? -sp->current_depth : sp->current_depth));
694     }
695
696   glScalef (2.0, 2.0, 2.0);
697
698   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
699   glCallList (sp->sponge_list0);
700   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
701   glCallList (sp->sponge_list1);
702   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
703   glCallList (sp->sponge_list2);
704
705   glPopMatrix ();
706
707   if (mi->fps_p) do_fps (mi);
708   glFinish();
709
710   glXSwapBuffers(dpy, window);
711 }
712
713 #endif /* USE_GL */