From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / menger.c
1 /* menger, Copyright (c) 2001-2014 Jamie Zawinski <jwz@jwz.org>
2  *         Copyright (c) 2002 Aurelien Jacobs <aurel@gnuage.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  *
12  * Generates a 3D Menger Sponge gasket:
13  *
14  *                                ___+._______
15  *                           __-""   --     __"""----._____
16  *                    __.--"" -- ___--+---_____.     __  .+'|
17  *              _.-'""  __    +:"__   | ._..+"" __    .+'   F
18  *            J"--.____         __ """""+"         .+'  .J  F
19  *            J        """""---.___       --   .+'"     F'  F
20  *             L                   """""--...+'    .J       F
21  *             L   F"9      --.            |   .   F'      J
22  *             L   -_J      L_J      F"9   | ;'J    .+J .J J
23  *             |                     L_J   | F.'  .'| J F' J
24  *             |        |"""--.__          | '   |""  J    J
25  *             J   ._   J ;;; |  "L        |   . |-___J    |
26  *             J   L J  J ;-' |   L        | .'J |_  .'  . |
27  *             J    ""  J    .---_L  F"9   | F.' | .'   FJ |
28  *              L       J .-'  __ |  L_J   | '   :'     ' .+
29  *              L       '--.___   |        |       .J   .'
30  *              |  F"9         """'        |   .   F' .'
31  *              |  -_J      F"9            | .'J    .'
32  *              +__         -_J      F"9   | F.'  .'
33  *                 """--___          L_J   | '  .'
34  *                         """---___       |  .'
35  *                                  ""---._|.'
36  *
37  *  The straightforward way to generate this object creates way more polygons
38  *  than are needed, since there end up being many buried, interior faces.
39  *  So during the recursive building of the object we store which face of
40  *  each unitary cube we need to draw. Doing this reduces the polygon count
41  *  by 40% - 60%.
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 actually simulate large depths with a texture map -- if the
49  *  depth is such that the smallest holes are only a few pixels across,
50  *  just draw them as spots on the surface!  It would look the same.
51  */
52
53 #define DEFAULTS        "*delay:         30000          \n" \
54                         "*showFPS:       False          \n" \
55                         "*wireframe:     False          \n" \
56                         "*suppressRotationAnimation: True\n" \
57
58
59 # define refresh_sponge 0
60 # define release_sponge 0
61 #undef countof
62 #define countof(x) (sizeof((x))/sizeof((*x)))
63
64 #include "xlockmore.h"
65 #include "colors.h"
66 #include "rotator.h"
67 #include "gltrackball.h"
68 #include <ctype.h>
69
70 #ifdef USE_GL /* whole file */
71
72 #define DEF_SPIN        "True"
73 #define DEF_WANDER      "True"
74 #define DEF_SPEED       "150"
75 #define DEF_MAX_DEPTH   "3"
76
77 typedef struct {
78   GLXContext *glx_context;
79   rotator *rot;
80   trackball_state *trackball;
81   Bool button_down_p;
82   GLuint sponge_list0;            /* we store X, Y, and Z-facing surfaces */
83   GLuint sponge_list1;            /* in their own lists, to make it easy  */
84   GLuint sponge_list2;            /* to color them differently.           */
85
86   unsigned long squares_fp;
87
88   int current_depth;
89
90   int ncolors;
91   XColor *colors;
92   int ccolor0;
93   int ccolor1;
94   int ccolor2;
95
96   int draw_tick;
97
98 } sponge_configuration;
99
100 static sponge_configuration *sps = NULL;
101
102 static Bool do_spin;
103 static Bool do_wander;
104 static int speed;
105 static int max_depth;
106
107 static XrmOptionDescRec opts[] = {
108   { "-wander", ".wander",   XrmoptionNoArg, "True"  },
109   { "+wander", ".wander",   XrmoptionNoArg, "False" },
110   { "-spin",   ".spin",     XrmoptionSepArg, 0 },
111   { "-speed",  ".speed",    XrmoptionSepArg, 0 },
112   { "-depth",  ".maxDepth", XrmoptionSepArg, 0 },
113 };
114
115 static argtype vars[] = {
116   {&do_spin,     "spin",     "Spin",     DEF_SPIN,      t_Bool},
117   {&do_wander,   "wander",   "Wander",   DEF_WANDER,    t_Bool},
118   {&speed,       "speed",    "Speed",    DEF_SPEED,     t_Int},
119   {&max_depth,   "maxDepth", "MaxDepth", DEF_MAX_DEPTH, t_Int},
120 };
121
122 ENTRYPOINT ModeSpecOpt sponge_opts = {countof(opts), opts, countof(vars), vars, NULL};
123
124
125 /* Window management, etc
126  */
127 ENTRYPOINT void
128 reshape_sponge (ModeInfo *mi, int width, int height)
129 {
130   GLfloat h = (GLfloat) height / (GLfloat) width;
131
132   glViewport (0, 0, (GLint) width, (GLint) height);
133
134   glMatrixMode(GL_PROJECTION);
135   glLoadIdentity();
136   gluPerspective (30.0, 1/h, 1.0, 100.0);
137
138   glMatrixMode(GL_MODELVIEW);
139   glLoadIdentity();
140   gluLookAt( 0.0, 0.0, 30.0,
141              0.0, 0.0, 0.0,
142              0.0, 1.0, 0.0);
143
144 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
145   {
146     int o = (int) current_device_rotation();
147     if (o != 0 && o != 180 && o != -180)
148       glScalef (1/h, 1/h, 1/h);
149   }
150 # endif
151
152   glClear(GL_COLOR_BUFFER_BIT);
153 }
154
155
156 #define X0 0x01
157 #define X1 0x02
158 #define Y0 0x04
159 #define Y1 0x08
160 #define Z0 0x10
161 #define Z1 0x20
162
163 static int
164 cube (float x0, float x1, float y0, float y1, float z0, float z1,
165       int faces, int wireframe)
166 {
167   int n = 0;
168
169   if (faces & X0)
170     {
171       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
172       glNormal3f (-1.0, 0.0, 0.0);
173       glVertex3f (x0, y1, z0);
174       glVertex3f (x0, y0, z0);
175       glVertex3f (x0, y0, z1);
176       glVertex3f (x0, y1, z1);
177       glEnd ();
178       n++;
179     }
180   if (faces & X1)
181     {
182       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
183       glNormal3f (1.0, 0.0, 0.0);
184       glVertex3f (x1, y1, z1);
185       glVertex3f (x1, y0, z1);
186       glVertex3f (x1, y0, z0);
187       glVertex3f (x1, y1, z0);
188       glEnd ();
189       n++;
190     }
191   if (faces & Y0)
192     {
193       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
194       glNormal3f (0.0, -1.0, 0.0);
195       glVertex3f (x0, y0, z0);
196       glVertex3f (x0, y0, z1);
197       glVertex3f (x1, y0, z1);
198       glVertex3f (x1, y0, z0);
199       glEnd ();
200       n++;
201     }
202   if (faces & Y1)
203     {
204       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
205       glNormal3f (0.0, 1.0, 0.0);
206       glVertex3f (x0, y1, z0);
207       glVertex3f (x0, y1, z1);
208       glVertex3f (x1, y1, z1);
209       glVertex3f (x1, y1, z0);
210       glEnd ();
211       n++;
212     }
213   if (faces & Z0)
214     {
215       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
216       glNormal3f (0.0, 0.0, -1.0);
217       glVertex3f (x1, y1, z0);
218       glVertex3f (x1, y0, z0);
219       glVertex3f (x0, y0, z0);
220       glVertex3f (x0, y1, z0);
221       glEnd ();
222       n++;
223     }
224   if (faces & Z1)
225     {
226       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
227       glNormal3f (0.0, 0.0, 1.0);
228       glVertex3f (x0, y1, z1);
229       glVertex3f (x0, y0, z1);
230       glVertex3f (x1, y0, z1);
231       glVertex3f (x1, y1, z1);
232       glEnd ();
233       n++;
234     }
235
236   return n;
237 }
238
239 static int
240 menger_recurs_1 (int level, float x0, float x1, float y0, float y1,
241                  float z0, float z1, int faces, Bool wireframe, 
242                  int orig, int forig)
243 {
244   float xi, yi, zi;
245   int f, x, y, z;
246   int n = 0;
247
248   if (orig)
249     {
250       if (wireframe)
251         n += cube (x0, x1, y0, y1, z0, z1,
252                    faces & (X0 | X1 | Y0 | Y1), wireframe);
253     }
254
255   if (level == 0)
256     {
257       if (!wireframe)
258         n += cube (x0, x1, y0, y1, z0, z1, faces, wireframe);
259     }
260   else
261     {
262       xi = (x1 - x0) / 3;
263       yi = (y1 - y0) / 3;
264       zi = (z1 - z0) / 3;
265
266       for (x = 0; x < 3; x++)
267         for (y = 0; y < 3; y++)
268           for (z = 0; z < 3; z++)
269             {
270               if ((x != 1 && y != 1)
271                   || (y != 1 && z != 1)
272                   || (x != 1 && z != 1))
273                 {
274                   f = faces;
275
276                   if (x == 1 || (x == 2 && (y != 1 && z != 1)))
277                     f &= ~X0;
278                   if (x == 1 || (x == 0 && (y != 1 && z != 1)))
279                     f &= ~X1;
280                   if (forig & X0 && x == 2 && (y == 1 || z == 1))
281                     f |= X0;
282                   if (forig & X1 && x == 0 && (y == 1 || z == 1))
283                     f |= X1;
284
285                   if (y == 1 || (y == 2 && (x != 1 && z != 1)))
286                     f &= ~Y0;
287                   if (y == 1 || (y == 0 && (x != 1 && z != 1)))
288                     f &= ~Y1;
289                   if (forig & Y0 && y == 2 && (x == 1 || z == 1))
290                     f |= Y0;
291                   if (forig & Y1 && y == 0 && (x == 1 || z == 1))
292                     f |= Y1;
293
294                   if (z == 1 || (z == 2 && (x != 1 && y != 1)))
295                     f &= ~Z0;
296                   if (z == 1 || (z == 0 && (x != 1 && y != 1)))
297                     f &= ~Z1;
298                   if (forig & Z0 && z == 2 && (x == 1 || y == 1))
299                     f |= Z0;
300                   if (forig & Z1 && z == 0 && (x == 1 || y == 1))
301                     f |= Z1;
302
303                   n += menger_recurs_1 (level-1,
304                                         x0+x*xi, x0+(x+1)*xi,
305                                         y0+y*yi, y0+(y+1)*yi,
306                                         z0+z*zi, z0+(z+1)*zi, f, wireframe, 0,
307                                         forig);
308                 }
309               else if (wireframe && (x != 1 || y != 1 || z != 1))
310                 n += cube (x0+x*xi, x0+(x+1)*xi,
311                            y0+y*yi, y0+(y+1)*yi,
312                            z0+z*zi, z0+(z+1)*zi,
313                            forig & (X0 | X1 | Y0 | Y1), wireframe);
314             }
315     }
316
317   return n;
318 }
319
320 static int
321 menger_recurs (int level, float x0, float x1, float y0, float y1,
322                float z0, float z1, int faces, Bool wireframe, 
323                int orig)
324 {
325   return menger_recurs_1 (level, x0, x1, y0, y1, z0, z1, faces, 
326                           wireframe, orig, faces);
327 }
328
329
330 static void
331 build_sponge (sponge_configuration *sp, Bool wireframe, int level)
332 {
333   glDeleteLists (sp->sponge_list0, 1);
334   glNewList(sp->sponge_list0, GL_COMPILE);
335   sp->squares_fp = menger_recurs (level, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5,
336                                   X0 | X1, wireframe,1);
337   glEndList();
338
339   glDeleteLists (sp->sponge_list1, 1);
340   glNewList(sp->sponge_list1, GL_COMPILE);
341   sp->squares_fp += menger_recurs (level, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5,
342                                    Y0 | Y1, wireframe,1);
343   glEndList();
344
345   glDeleteLists (sp->sponge_list2, 1);
346   glNewList(sp->sponge_list2, GL_COMPILE);
347   sp->squares_fp += menger_recurs (level, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5,
348                                    Z0 | Z1, wireframe,1);
349   glEndList();
350 }
351
352
353 ENTRYPOINT Bool
354 sponge_handle_event (ModeInfo *mi, XEvent *event)
355 {
356   sponge_configuration *sp = &sps[MI_SCREEN(mi)];
357
358   if (gltrackball_event_handler (event, sp->trackball,
359                                  MI_WIDTH (mi), MI_HEIGHT (mi),
360                                  &sp->button_down_p))
361     return True;
362   else if (event->xany.type == KeyPress)
363     {
364       KeySym keysym;
365       char c = 0;
366       XLookupString (&event->xkey, &c, 1, &keysym, 0);
367       if (c == '+' || c == '=' ||
368           keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
369         {
370           sp->draw_tick = speed;
371           sp->current_depth += (sp->current_depth > 0 ? 1 : -1);
372           sp->current_depth--;
373           return True;
374         }
375       else if (c == '-' || c == '_' ||
376                keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
377         {
378           sp->draw_tick = speed;
379           sp->current_depth -= (sp->current_depth > 0 ? 1 : -1);
380           sp->current_depth--;
381           return True;
382         }
383       else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
384         goto DEF;
385     }
386   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
387     {
388     DEF:
389       sp->draw_tick = speed;
390       return True;
391     }
392
393   return False;
394 }
395
396
397
398 ENTRYPOINT void 
399 init_sponge (ModeInfo *mi)
400 {
401   sponge_configuration *sp;
402   int wire = MI_IS_WIREFRAME(mi);
403
404   MI_INIT (mi, sps, NULL);
405
406   sp = &sps[MI_SCREEN(mi)];
407
408   if ((sp->glx_context = init_GL(mi)) != NULL) {
409     reshape_sponge (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
410   }
411
412   if (!wire)
413     {
414       static const GLfloat pos0[4] = {-1.0, -1.0, 1.0, 0.1};
415       static const GLfloat pos1[4] = { 1.0, -0.2, 0.2, 0.1};
416       static const GLfloat dif0[4] = {1.0, 1.0, 1.0, 1.0};
417       static const GLfloat dif1[4] = {1.0, 1.0, 1.0, 1.0};
418
419       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
420       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
421       glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
422       glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
423
424       glEnable(GL_LIGHTING);
425       glEnable(GL_LIGHT0);
426       glEnable(GL_LIGHT1);
427
428       glEnable(GL_DEPTH_TEST);
429       glEnable(GL_NORMALIZE);
430
431       glShadeModel(GL_SMOOTH);
432     }
433
434   {
435     double spin_speed   = 1.0;
436     double wander_speed = 0.03;
437     sp->rot = make_rotator (do_spin ? spin_speed : 0,
438                             do_spin ? spin_speed : 0,
439                             do_spin ? spin_speed : 0,
440                             1.0,
441                             do_wander ? wander_speed : 0,
442                             True);
443     sp->trackball = gltrackball_init (True);
444   }
445
446   sp->ncolors = 128;
447   sp->colors = (XColor *) calloc(sp->ncolors, sizeof(XColor));
448   make_smooth_colormap (0, 0, 0,
449                         sp->colors, &sp->ncolors,
450                         False, 0, False);
451   sp->ccolor0 = 0;
452   sp->ccolor1 = sp->ncolors / 3;
453   sp->ccolor2 = sp->ccolor1 * 2;
454
455   sp->sponge_list0 = glGenLists (1);
456   sp->sponge_list1 = glGenLists (1);
457   sp->sponge_list2 = glGenLists (1);
458
459   sp->draw_tick = 9999999;
460 }
461
462
463 ENTRYPOINT void
464 draw_sponge (ModeInfo *mi)
465 {
466   sponge_configuration *sp = &sps[MI_SCREEN(mi)];
467   Display *dpy = MI_DISPLAY(mi);
468   Window window = MI_WINDOW(mi);
469
470   GLfloat color0[4] = {0.0, 0.0, 0.0, 1.0};
471   GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
472   GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
473
474   if (!sp->glx_context)
475     return;
476
477   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
478
479   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
480
481   glPushMatrix ();
482
483   glScalef(1.1, 1.1, 1.1);
484
485   {
486     double x, y, z;
487     get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
488     glTranslatef((x - 0.5) * 8,
489                  (y - 0.5) * 6,
490                  (z - 0.5) * 15);
491
492     gltrackball_rotate (sp->trackball);
493
494     get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
495     glRotatef (x * 360, 1.0, 0.0, 0.0);
496     glRotatef (y * 360, 0.0, 1.0, 0.0);
497     glRotatef (z * 360, 0.0, 0.0, 1.0);
498   }
499
500   color0[0] = sp->colors[sp->ccolor0].red   / 65536.0;
501   color0[1] = sp->colors[sp->ccolor0].green / 65536.0;
502   color0[2] = sp->colors[sp->ccolor0].blue  / 65536.0;
503
504   color1[0] = sp->colors[sp->ccolor1].red   / 65536.0;
505   color1[1] = sp->colors[sp->ccolor1].green / 65536.0;
506   color1[2] = sp->colors[sp->ccolor1].blue  / 65536.0;
507
508   color2[0] = sp->colors[sp->ccolor2].red   / 65536.0;
509   color2[1] = sp->colors[sp->ccolor2].green / 65536.0;
510   color2[2] = sp->colors[sp->ccolor2].blue  / 65536.0;
511
512
513   sp->ccolor0++;
514   sp->ccolor1++;
515   sp->ccolor2++;
516   if (sp->ccolor0 >= sp->ncolors) sp->ccolor0 = 0;
517   if (sp->ccolor1 >= sp->ncolors) sp->ccolor1 = 0;
518   if (sp->ccolor2 >= sp->ncolors) sp->ccolor2 = 0;
519
520   if (sp->draw_tick++ >= speed)
521     {
522       sp->draw_tick = 0;
523       if (sp->current_depth >= max_depth)
524         sp->current_depth = -max_depth;
525       sp->current_depth++;
526       build_sponge (sp,
527                     MI_IS_WIREFRAME(mi),
528                     (sp->current_depth < 0
529                      ? -sp->current_depth : sp->current_depth));
530
531       mi->polygon_count = sp->squares_fp;  /* for FPS display */
532       mi->recursion_depth = (sp->current_depth < 0
533                              ? -sp->current_depth : sp->current_depth);
534     }
535
536   glScalef (2.0, 2.0, 2.0);
537
538   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
539   glCallList (sp->sponge_list0);
540   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
541   glCallList (sp->sponge_list1);
542   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
543   glCallList (sp->sponge_list2);
544
545   glPopMatrix ();
546
547   if (mi->fps_p) do_fps (mi);
548   glFinish();
549
550   glXSwapBuffers(dpy, window);
551 }
552
553 XSCREENSAVER_MODULE_2 ("Menger", menger, sponge)
554
555 #endif /* USE_GL */