ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.21.tar.gz
[xscreensaver] / hacks / glx / menger.c
1 /* menger, Copyright (c) 2001, 2002, 2004 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 #include <X11/Intrinsic.h>
54
55 extern XtAppContext app;
56
57 #define PROGCLASS       "Menger"
58 #define HACK_INIT       init_sponge
59 #define HACK_DRAW       draw_sponge
60 #define HACK_RESHAPE    reshape_sponge
61 #define HACK_HANDLE_EVENT sponge_handle_event
62 #define EVENT_MASK      PointerMotionMask
63 #define sws_opts        xlockmore_opts
64
65 #define DEF_SPIN        "True"
66 #define DEF_WANDER      "True"
67 #define DEF_SPEED       "150"
68 #define DEF_MAX_DEPTH   "3"
69
70 #define DEFAULTS        "*delay:         30000          \n" \
71                         "*showFPS:       False          \n" \
72                         "*wireframe:     False          \n" \
73
74
75 #undef countof
76 #define countof(x) (sizeof((x))/sizeof((*x)))
77
78 #include "xlockmore.h"
79 #include "colors.h"
80 #include "rotator.h"
81 #include "gltrackball.h"
82 #include <ctype.h>
83
84 #ifdef USE_GL /* whole file */
85
86 #include <GL/glu.h>
87
88
89 typedef struct {
90   GLXContext *glx_context;
91   rotator *rot;
92   trackball_state *trackball;
93   Bool button_down_p;
94   GLuint sponge_list0;            /* we store X, Y, and Z-facing surfaces */
95   GLuint sponge_list1;            /* in their own lists, to make it easy  */
96   GLuint sponge_list2;            /* to color them differently.           */
97
98   unsigned long squares_fp;
99
100   int current_depth;
101
102   int ncolors;
103   XColor *colors;
104   int ccolor0;
105   int ccolor1;
106   int ccolor2;
107
108 } sponge_configuration;
109
110 static sponge_configuration *sps = NULL;
111
112 static Bool do_spin;
113 static Bool do_wander;
114 static int speed;
115 static int max_depth;
116
117 static XrmOptionDescRec opts[] = {
118   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
119   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
120   { "-wander", ".wander", XrmoptionNoArg, "True" },
121   { "+wander", ".wander", XrmoptionNoArg, "False" },
122   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
123   {"-depth",   ".maxDepth", XrmoptionSepArg, 0 },
124 };
125
126 static argtype vars[] = {
127   {&do_spin,     "spin",     "Spin",     DEF_SPIN,      t_Bool},
128   {&do_wander,   "wander",   "Wander",   DEF_WANDER,    t_Bool},
129   {&speed,       "speed",    "Speed",    DEF_SPEED,     t_Int},
130   {&max_depth,   "maxDepth", "MaxDepth", DEF_MAX_DEPTH, t_Int},
131 };
132
133 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
134
135
136 /* Window management, etc
137  */
138 void
139 reshape_sponge (ModeInfo *mi, int width, int height)
140 {
141   GLfloat h = (GLfloat) height / (GLfloat) width;
142
143   glViewport (0, 0, (GLint) width, (GLint) height);
144
145   glMatrixMode(GL_PROJECTION);
146   glLoadIdentity();
147   gluPerspective (30.0, 1/h, 1.0, 100.0);
148
149   glMatrixMode(GL_MODELVIEW);
150   glLoadIdentity();
151   gluLookAt( 0.0, 0.0, 30.0,
152              0.0, 0.0, 0.0,
153              0.0, 1.0, 0.0);
154
155   glClear(GL_COLOR_BUFFER_BIT);
156 }
157
158
159 #define X0 0x01
160 #define X1 0x02
161 #define Y0 0x04
162 #define Y1 0x08
163 #define Z0 0x10
164 #define Z1 0x20
165
166 static int
167 cube (float x0, float x1, float y0, float y1, float z0, float z1,
168       int faces, int wireframe)
169 {
170   int n = 0;
171
172   if (faces & X0)
173     {
174       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
175       glNormal3f (-1.0, 0.0, 0.0);
176       glVertex3f (x0, y1, z0);
177       glVertex3f (x0, y0, z0);
178       glVertex3f (x0, y0, z1);
179       glVertex3f (x0, y1, z1);
180       glEnd ();
181       n++;
182     }
183   if (faces & X1)
184     {
185       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
186       glNormal3f (1.0, 0.0, 0.0);
187       glVertex3f (x1, y1, z1);
188       glVertex3f (x1, y0, z1);
189       glVertex3f (x1, y0, z0);
190       glVertex3f (x1, y1, z0);
191       glEnd ();
192       n++;
193     }
194   if (faces & Y0)
195     {
196       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
197       glNormal3f (0.0, -1.0, 0.0);
198       glVertex3f (x0, y0, z0);
199       glVertex3f (x0, y0, z1);
200       glVertex3f (x1, y0, z1);
201       glVertex3f (x1, y0, z0);
202       glEnd ();
203       n++;
204     }
205   if (faces & Y1)
206     {
207       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
208       glNormal3f (0.0, 1.0, 0.0);
209       glVertex3f (x0, y1, z0);
210       glVertex3f (x0, y1, z1);
211       glVertex3f (x1, y1, z1);
212       glVertex3f (x1, y1, z0);
213       glEnd ();
214       n++;
215     }
216   if (faces & Z0)
217     {
218       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
219       glNormal3f (0.0, 0.0, -1.0);
220       glVertex3f (x1, y1, z0);
221       glVertex3f (x1, y0, z0);
222       glVertex3f (x0, y0, z0);
223       glVertex3f (x0, y1, z0);
224       glEnd ();
225       n++;
226     }
227   if (faces & Z1)
228     {
229       glBegin (wireframe ? GL_LINE_LOOP : GL_POLYGON);
230       glNormal3f (0.0, 0.0, 1.0);
231       glVertex3f (x0, y1, z1);
232       glVertex3f (x0, y0, z1);
233       glVertex3f (x1, y0, z1);
234       glVertex3f (x1, y1, z1);
235       glEnd ();
236       n++;
237     }
238
239   return n;
240 }
241
242 static int
243 menger_recurs (int level, float x0, float x1, float y0, float y1,
244                float z0, float z1, int faces, Bool wireframe, int orig)
245 {
246   float xi, yi, zi;
247   int f, x, y, z;
248   static int forig;
249   int n = 0;
250
251   if (orig)
252     {
253       forig = faces;
254       if (wireframe)
255         n += cube (x0, x1, y0, y1, z0, z1,
256                    faces & (X0 | X1 | Y0 | Y1), wireframe);
257     }
258
259   if (level == 0)
260     {
261       if (!wireframe)
262         n += cube (x0, x1, y0, y1, z0, z1, faces, wireframe);
263     }
264   else
265     {
266       xi = (x1 - x0) / 3;
267       yi = (y1 - y0) / 3;
268       zi = (z1 - z0) / 3;
269
270       for (x = 0; x < 3; x++)
271         for (y = 0; y < 3; y++)
272           for (z = 0; z < 3; z++)
273             {
274               if ((x != 1 && y != 1)
275                   || (y != 1 && z != 1)
276                   || (x != 1 && z != 1))
277                 {
278                   f = faces;
279
280                   if (x == 1 || (x == 2 && (y != 1 && z != 1)))
281                     f &= ~X0;
282                   if (x == 1 || (x == 0 && (y != 1 && z != 1)))
283                     f &= ~X1;
284                   if (forig & X0 && x == 2 && (y == 1 || z == 1))
285                     f |= X0;
286                   if (forig & X1 && x == 0 && (y == 1 || z == 1))
287                     f |= X1;
288
289                   if (y == 1 || (y == 2 && (x != 1 && z != 1)))
290                     f &= ~Y0;
291                   if (y == 1 || (y == 0 && (x != 1 && z != 1)))
292                     f &= ~Y1;
293                   if (forig & Y0 && y == 2 && (x == 1 || z == 1))
294                     f |= Y0;
295                   if (forig & Y1 && y == 0 && (x == 1 || z == 1))
296                     f |= Y1;
297
298                   if (z == 1 || (z == 2 && (x != 1 && y != 1)))
299                     f &= ~Z0;
300                   if (z == 1 || (z == 0 && (x != 1 && y != 1)))
301                     f &= ~Z1;
302                   if (forig & Z0 && z == 2 && (x == 1 || y == 1))
303                     f |= Z0;
304                   if (forig & Z1 && z == 0 && (x == 1 || y == 1))
305                     f |= Z1;
306
307                   n += menger_recurs (level-1,
308                                       x0+x*xi, x0+(x+1)*xi,
309                                       y0+y*yi, y0+(y+1)*yi,
310                                       z0+z*zi, z0+(z+1)*zi, f, wireframe, 0);
311                 }
312               else if (wireframe && (x != 1 || y != 1 || z != 1))
313                 n += cube (x0+x*xi, x0+(x+1)*xi,
314                            y0+y*yi, y0+(y+1)*yi,
315                            z0+z*zi, z0+(z+1)*zi,
316                            forig & (X0 | X1 | Y0 | Y1), wireframe);
317             }
318     }
319
320   return n;
321 }
322
323 static void
324 build_sponge (sponge_configuration *sp, Bool wireframe, int level)
325 {
326   glDeleteLists (sp->sponge_list0, 1);
327   glNewList(sp->sponge_list0, GL_COMPILE);
328   sp->squares_fp = menger_recurs (level, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5,
329                                   X0 | X1, wireframe,1);
330   glEndList();
331
332   glDeleteLists (sp->sponge_list1, 1);
333   glNewList(sp->sponge_list1, GL_COMPILE);
334   sp->squares_fp += menger_recurs (level, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5,
335                                    Y0 | Y1, wireframe,1);
336   glEndList();
337
338   glDeleteLists (sp->sponge_list2, 1);
339   glNewList(sp->sponge_list2, GL_COMPILE);
340   sp->squares_fp += menger_recurs (level, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5,
341                                    Z0 | Z1, wireframe,1);
342   glEndList();
343 }
344
345
346 Bool
347 sponge_handle_event (ModeInfo *mi, XEvent *event)
348 {
349   sponge_configuration *sp = &sps[MI_SCREEN(mi)];
350
351   if (event->xany.type == ButtonPress &&
352       event->xbutton.button == Button1)
353     {
354       sp->button_down_p = True;
355       gltrackball_start (sp->trackball,
356                          event->xbutton.x, event->xbutton.y,
357                          MI_WIDTH (mi), MI_HEIGHT (mi));
358       return True;
359     }
360   else if (event->xany.type == ButtonRelease &&
361            event->xbutton.button == Button1)
362     {
363       sp->button_down_p = False;
364       return True;
365     }
366   else if (event->xany.type == ButtonPress &&
367            (event->xbutton.button == Button4 ||
368             event->xbutton.button == Button5))
369     {
370       gltrackball_mousewheel (sp->trackball, event->xbutton.button, 5,
371                               !!event->xbutton.state);
372       return True;
373     }
374   else if (event->xany.type == MotionNotify &&
375            sp->button_down_p)
376     {
377       gltrackball_track (sp->trackball,
378                          event->xmotion.x, event->xmotion.y,
379                          MI_WIDTH (mi), MI_HEIGHT (mi));
380       return True;
381     }
382
383   return False;
384 }
385
386
387
388 void 
389 init_sponge (ModeInfo *mi)
390 {
391   sponge_configuration *sp;
392   int wire = MI_IS_WIREFRAME(mi);
393
394   if (!sps) {
395     sps = (sponge_configuration *)
396       calloc (MI_NUM_SCREENS(mi), sizeof (sponge_configuration));
397     if (!sps) {
398       fprintf(stderr, "%s: out of memory\n", progname);
399       exit(1);
400     }
401
402     sp = &sps[MI_SCREEN(mi)];
403   }
404
405   sp = &sps[MI_SCREEN(mi)];
406
407   if ((sp->glx_context = init_GL(mi)) != NULL) {
408     reshape_sponge (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
409   }
410
411   if (!wire)
412     {
413       static GLfloat pos0[4] = {-1.0, -1.0, 1.0, 0.1};
414       static GLfloat pos1[4] = { 1.0, -0.2, 0.2, 0.1};
415       static GLfloat dif0[4] = {1.0, 1.0, 1.0, 1.0};
416       static GLfloat dif1[4] = {1.0, 1.0, 1.0, 1.0};
417
418       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
419       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
420       glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
421       glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
422
423       glEnable(GL_LIGHTING);
424       glEnable(GL_LIGHT0);
425       glEnable(GL_LIGHT1);
426
427       glEnable(GL_DEPTH_TEST);
428       glEnable(GL_NORMALIZE);
429
430       glShadeModel(GL_SMOOTH);
431     }
432
433   {
434     double spin_speed   = 1.0;
435     double wander_speed = 0.03;
436     sp->rot = make_rotator (do_spin ? spin_speed : 0,
437                             do_spin ? spin_speed : 0,
438                             do_spin ? spin_speed : 0,
439                             1.0,
440                             do_wander ? wander_speed : 0,
441                             True);
442     sp->trackball = gltrackball_init ();
443   }
444
445   sp->ncolors = 128;
446   sp->colors = (XColor *) calloc(sp->ncolors, sizeof(XColor));
447   make_smooth_colormap (0, 0, 0,
448                         sp->colors, &sp->ncolors,
449                         False, 0, False);
450   sp->ccolor0 = 0;
451   sp->ccolor1 = sp->ncolors / 3;
452   sp->ccolor2 = sp->ccolor1 * 2;
453
454   sp->sponge_list0 = glGenLists (1);
455   sp->sponge_list1 = glGenLists (1);
456   sp->sponge_list2 = glGenLists (1);
457 }
458
459
460 void
461 draw_sponge (ModeInfo *mi)
462 {
463   sponge_configuration *sp = &sps[MI_SCREEN(mi)];
464   Display *dpy = MI_DISPLAY(mi);
465   Window window = MI_WINDOW(mi);
466
467   static GLfloat color0[4] = {0.0, 0.0, 0.0, 1.0};
468   static GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
469   static GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
470
471   static int tick = 99999;
472
473   if (!sp->glx_context)
474     return;
475
476   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
477
478   glPushMatrix ();
479
480   glScalef(1.1, 1.1, 1.1);
481
482   {
483     double x, y, z;
484     get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
485     glTranslatef((x - 0.5) * 8,
486                  (y - 0.5) * 6,
487                  (z - 0.5) * 15);
488
489     gltrackball_rotate (sp->trackball);
490
491     get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
492     glRotatef (x * 360, 1.0, 0.0, 0.0);
493     glRotatef (y * 360, 0.0, 1.0, 0.0);
494     glRotatef (z * 360, 0.0, 0.0, 1.0);
495   }
496
497   color0[0] = sp->colors[sp->ccolor0].red   / 65536.0;
498   color0[1] = sp->colors[sp->ccolor0].green / 65536.0;
499   color0[2] = sp->colors[sp->ccolor0].blue  / 65536.0;
500
501   color1[0] = sp->colors[sp->ccolor1].red   / 65536.0;
502   color1[1] = sp->colors[sp->ccolor1].green / 65536.0;
503   color1[2] = sp->colors[sp->ccolor1].blue  / 65536.0;
504
505   color2[0] = sp->colors[sp->ccolor2].red   / 65536.0;
506   color2[1] = sp->colors[sp->ccolor2].green / 65536.0;
507   color2[2] = sp->colors[sp->ccolor2].blue  / 65536.0;
508
509
510   sp->ccolor0++;
511   sp->ccolor1++;
512   sp->ccolor2++;
513   if (sp->ccolor0 >= sp->ncolors) sp->ccolor0 = 0;
514   if (sp->ccolor1 >= sp->ncolors) sp->ccolor1 = 0;
515   if (sp->ccolor2 >= sp->ncolors) sp->ccolor2 = 0;
516
517   if (tick++ >= speed)
518     {
519       tick = 0;
520       if (sp->current_depth >= max_depth)
521         sp->current_depth = -max_depth;
522       sp->current_depth++;
523       build_sponge (sp,
524                     MI_IS_WIREFRAME(mi),
525                     (sp->current_depth < 0
526                      ? -sp->current_depth : sp->current_depth));
527
528       mi->polygon_count = sp->squares_fp;  /* for FPS display */
529     }
530
531   glScalef (2.0, 2.0, 2.0);
532
533   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
534   glCallList (sp->sponge_list0);
535   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
536   glCallList (sp->sponge_list1);
537   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
538   glCallList (sp->sponge_list2);
539
540   glPopMatrix ();
541
542   if (mi->fps_p) do_fps (mi);
543   glFinish();
544
545   glXSwapBuffers(dpy, window);
546 }
547
548 #endif /* USE_GL */