1 /* menger, Copyright (c) 2001-2014 Jamie Zawinski <jwz@jwz.org>
2 * Copyright (c) 2002 Aurelien Jacobs <aurel@gnuage.org>
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
12 * Generates a 3D Menger Sponge gasket:
15 * __-"" -- __"""----._____
16 * __.--"" -- ___--+---_____. __ .+'|
17 * _.-'"" __ +:"__ | ._..+"" __ .+' F
18 * J"--.____ __ """""+" .+' .J F
19 * J """""---.___ -- .+'" F' F
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 | ' :' ' .+
30 * | F"9 """' | . F' .'
32 * +__ -_J F"9 | F.' .'
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
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.
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.
53 #define DEFAULTS "*delay: 30000 \n" \
54 "*showFPS: False \n" \
55 "*wireframe: False \n" \
56 "*suppressRotationAnimation: True\n" \
59 # define refresh_sponge 0
60 # define release_sponge 0
62 #define countof(x) (sizeof((x))/sizeof((*x)))
64 #include "xlockmore.h"
67 #include "gltrackball.h"
70 #ifdef USE_GL /* whole file */
72 #define DEF_SPIN "True"
73 #define DEF_WANDER "True"
74 #define DEF_SPEED "150"
75 #define DEF_MAX_DEPTH "3"
78 GLXContext *glx_context;
80 trackball_state *trackball;
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. */
86 unsigned long squares_fp;
98 } sponge_configuration;
100 static sponge_configuration *sps = NULL;
103 static Bool do_wander;
105 static int max_depth;
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 },
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},
122 ENTRYPOINT ModeSpecOpt sponge_opts = {countof(opts), opts, countof(vars), vars, NULL};
125 /* Window management, etc
128 reshape_sponge (ModeInfo *mi, int width, int height)
130 GLfloat h = (GLfloat) height / (GLfloat) width;
132 glViewport (0, 0, (GLint) width, (GLint) height);
134 glMatrixMode(GL_PROJECTION);
136 gluPerspective (30.0, 1/h, 1.0, 100.0);
138 glMatrixMode(GL_MODELVIEW);
140 gluLookAt( 0.0, 0.0, 30.0,
144 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
146 int o = (int) current_device_rotation();
147 if (o != 0 && o != 180 && o != -180)
148 glScalef (1/h, 1/h, 1/h);
152 glClear(GL_COLOR_BUFFER_BIT);
164 cube (float x0, float x1, float y0, float y1, float z0, float z1,
165 int faces, int wireframe)
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);
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);
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);
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);
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);
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);
240 menger_recurs_1 (int level, float x0, float x1, float y0, float y1,
241 float z0, float z1, int faces, Bool wireframe,
251 n += cube (x0, x1, y0, y1, z0, z1,
252 faces & (X0 | X1 | Y0 | Y1), wireframe);
258 n += cube (x0, x1, y0, y1, z0, z1, faces, wireframe);
266 for (x = 0; x < 3; x++)
267 for (y = 0; y < 3; y++)
268 for (z = 0; z < 3; z++)
270 if ((x != 1 && y != 1)
271 || (y != 1 && z != 1)
272 || (x != 1 && z != 1))
276 if (x == 1 || (x == 2 && (y != 1 && z != 1)))
278 if (x == 1 || (x == 0 && (y != 1 && z != 1)))
280 if (forig & X0 && x == 2 && (y == 1 || z == 1))
282 if (forig & X1 && x == 0 && (y == 1 || z == 1))
285 if (y == 1 || (y == 2 && (x != 1 && z != 1)))
287 if (y == 1 || (y == 0 && (x != 1 && z != 1)))
289 if (forig & Y0 && y == 2 && (x == 1 || z == 1))
291 if (forig & Y1 && y == 0 && (x == 1 || z == 1))
294 if (z == 1 || (z == 2 && (x != 1 && y != 1)))
296 if (z == 1 || (z == 0 && (x != 1 && y != 1)))
298 if (forig & Z0 && z == 2 && (x == 1 || y == 1))
300 if (forig & Z1 && z == 0 && (x == 1 || y == 1))
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,
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);
321 menger_recurs (int level, float x0, float x1, float y0, float y1,
322 float z0, float z1, int faces, Bool wireframe,
325 return menger_recurs_1 (level, x0, x1, y0, y1, z0, z1, faces,
326 wireframe, orig, faces);
331 build_sponge (sponge_configuration *sp, Bool wireframe, int level)
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);
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);
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);
354 sponge_handle_event (ModeInfo *mi, XEvent *event)
356 sponge_configuration *sp = &sps[MI_SCREEN(mi)];
358 if (gltrackball_event_handler (event, sp->trackball,
359 MI_WIDTH (mi), MI_HEIGHT (mi),
362 else if (event->xany.type == KeyPress)
366 XLookupString (&event->xkey, &c, 1, &keysym, 0);
367 if (c == '+' || c == '=' ||
368 keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
370 sp->draw_tick = speed;
371 sp->current_depth += (sp->current_depth > 0 ? 1 : -1);
375 else if (c == '-' || c == '_' ||
376 keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
378 sp->draw_tick = speed;
379 sp->current_depth -= (sp->current_depth > 0 ? 1 : -1);
383 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
386 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
389 sp->draw_tick = speed;
399 init_sponge (ModeInfo *mi)
401 sponge_configuration *sp;
402 int wire = MI_IS_WIREFRAME(mi);
405 sps = (sponge_configuration *)
406 calloc (MI_NUM_SCREENS(mi), sizeof (sponge_configuration));
408 fprintf(stderr, "%s: out of memory\n", progname);
413 sp = &sps[MI_SCREEN(mi)];
415 if ((sp->glx_context = init_GL(mi)) != NULL) {
416 reshape_sponge (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
421 static const GLfloat pos0[4] = {-1.0, -1.0, 1.0, 0.1};
422 static const GLfloat pos1[4] = { 1.0, -0.2, 0.2, 0.1};
423 static const GLfloat dif0[4] = {1.0, 1.0, 1.0, 1.0};
424 static const GLfloat dif1[4] = {1.0, 1.0, 1.0, 1.0};
426 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
427 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
428 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
429 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
431 glEnable(GL_LIGHTING);
435 glEnable(GL_DEPTH_TEST);
436 glEnable(GL_NORMALIZE);
438 glShadeModel(GL_SMOOTH);
442 double spin_speed = 1.0;
443 double wander_speed = 0.03;
444 sp->rot = make_rotator (do_spin ? spin_speed : 0,
445 do_spin ? spin_speed : 0,
446 do_spin ? spin_speed : 0,
448 do_wander ? wander_speed : 0,
450 sp->trackball = gltrackball_init (True);
454 sp->colors = (XColor *) calloc(sp->ncolors, sizeof(XColor));
455 make_smooth_colormap (0, 0, 0,
456 sp->colors, &sp->ncolors,
459 sp->ccolor1 = sp->ncolors / 3;
460 sp->ccolor2 = sp->ccolor1 * 2;
462 sp->sponge_list0 = glGenLists (1);
463 sp->sponge_list1 = glGenLists (1);
464 sp->sponge_list2 = glGenLists (1);
466 sp->draw_tick = 9999999;
471 draw_sponge (ModeInfo *mi)
473 sponge_configuration *sp = &sps[MI_SCREEN(mi)];
474 Display *dpy = MI_DISPLAY(mi);
475 Window window = MI_WINDOW(mi);
477 GLfloat color0[4] = {0.0, 0.0, 0.0, 1.0};
478 GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
479 GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
481 if (!sp->glx_context)
484 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
486 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
490 glScalef(1.1, 1.1, 1.1);
494 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
495 glTranslatef((x - 0.5) * 8,
499 gltrackball_rotate (sp->trackball);
501 get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
502 glRotatef (x * 360, 1.0, 0.0, 0.0);
503 glRotatef (y * 360, 0.0, 1.0, 0.0);
504 glRotatef (z * 360, 0.0, 0.0, 1.0);
507 color0[0] = sp->colors[sp->ccolor0].red / 65536.0;
508 color0[1] = sp->colors[sp->ccolor0].green / 65536.0;
509 color0[2] = sp->colors[sp->ccolor0].blue / 65536.0;
511 color1[0] = sp->colors[sp->ccolor1].red / 65536.0;
512 color1[1] = sp->colors[sp->ccolor1].green / 65536.0;
513 color1[2] = sp->colors[sp->ccolor1].blue / 65536.0;
515 color2[0] = sp->colors[sp->ccolor2].red / 65536.0;
516 color2[1] = sp->colors[sp->ccolor2].green / 65536.0;
517 color2[2] = sp->colors[sp->ccolor2].blue / 65536.0;
523 if (sp->ccolor0 >= sp->ncolors) sp->ccolor0 = 0;
524 if (sp->ccolor1 >= sp->ncolors) sp->ccolor1 = 0;
525 if (sp->ccolor2 >= sp->ncolors) sp->ccolor2 = 0;
527 if (sp->draw_tick++ >= speed)
530 if (sp->current_depth >= max_depth)
531 sp->current_depth = -max_depth;
535 (sp->current_depth < 0
536 ? -sp->current_depth : sp->current_depth));
538 mi->polygon_count = sp->squares_fp; /* for FPS display */
539 mi->recursion_depth = (sp->current_depth < 0
540 ? -sp->current_depth : sp->current_depth);
543 glScalef (2.0, 2.0, 2.0);
545 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
546 glCallList (sp->sponge_list0);
547 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
548 glCallList (sp->sponge_list1);
549 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
550 glCallList (sp->sponge_list2);
554 if (mi->fps_p) do_fps (mi);
557 glXSwapBuffers(dpy, window);
560 XSCREENSAVER_MODULE_2 ("Menger", menger, sponge)