From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / sierpinski3d.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* Sierpinski3D --- 3D sierpinski gasket */
3
4 #if 0
5 static const char sccsid[] = "@(#)sierpinski3D.c        00.01 99/11/04 xlockmore";
6 #endif
7
8 /*-
9  * Permission to use, copy, modify, and distribute this software and its
10  * documentation for any purpose and without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and that
12  * both that copyright notice and this permission notice appear in
13  * supporting documentation.
14  *
15  * This file is provided AS IS with no warranties of any kind.  The author
16  * shall have no liability with respect to the infringement of copyrights,
17  * trade secrets or any patents by this file or any part thereof.  In no
18  * event will the author be liable for any lost revenue or profits or
19  * other special, indirect and consequential damages.
20  *
21  * Revision History:
22  * 1999: written by Tim Robinson <the_luggage@bigfoot.com>
23  *       a 3-D representation of the Sierpinski gasket fractal.
24  *
25  * 10-Dec-99  jwz   rewrote to draw a set of tetrahedrons instead of a
26  *                  random scattering of points.
27  */
28
29 #ifdef STANDALONE
30 # define DEFAULTS                                       "*delay:                20000   \n"                     \
31                                                                         "*showFPS:      False   \n"                     \
32                                                                         "*wireframe:    False   \n"                     \
33
34 # define refresh_gasket 0
35 # include "xlockmore.h"         /* from the xscreensaver distribution */
36 #else  /* !STANDALONE */
37 # include "xlock.h"                     /* from the xlockmore distribution */
38 #endif /* !STANDALONE */
39
40 #ifdef USE_GL
41
42 #define DEF_SPIN                                "True"
43 #define DEF_WANDER                              "True"
44 #define DEF_SPEED                               "150"
45 #define DEF_MAX_DEPTH                   "5"
46
47 #include "rotator.h"
48 #include "gltrackball.h"
49
50 #undef countof
51 #define countof(x) (sizeof((x))/sizeof((*x)))
52
53 static int max_depth;
54 static int speed;
55 static Bool do_spin;
56 static Bool do_wander;
57
58 static XrmOptionDescRec opts[] = {
59   {"-depth", ".sierpinski3d.maxDepth", XrmoptionSepArg, 0 },
60   {"-speed", ".sierpinski3d.speed",    XrmoptionSepArg, 0 },
61   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
62   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
63   { "-wander", ".wander", XrmoptionNoArg, "True" },
64   { "+wander", ".wander", XrmoptionNoArg, "False" },
65 };
66
67 static argtype vars[] = {
68   {&do_spin,   "spin",     "Spin",     DEF_SPIN,      t_Bool},
69   {&do_wander, "wander",   "Wander",   DEF_WANDER,    t_Bool},
70   {&speed,     "speed",    "Speed",    DEF_SPEED,     t_Int},
71   {&max_depth, "maxDepth", "MaxDepth", DEF_MAX_DEPTH, t_Int},
72 };
73
74
75 ENTRYPOINT ModeSpecOpt gasket_opts = {countof(opts), opts, countof(vars), vars, NULL};
76
77 #ifdef USE_MODULES
78 ModStruct   gasket_description =
79 {"gasket", "init_gasket", "draw_gasket", "release_gasket",
80  "draw_gasket", "init_gasket", NULL, &gasket_opts,
81  1000, 1, 2, 1, 4, 1.0, "",
82  "Shows GL's Sierpinski gasket", 0, NULL};
83
84 #endif
85
86 typedef struct {
87   double x,y,z;
88 } XYZ;
89
90 typedef struct {
91   GLuint      gasket0, gasket1, gasket2, gasket3;
92   GLXContext *glx_context;
93   Window      window;
94   rotator    *rot;
95   trackball_state *trackball;
96   Bool            button_down_p;
97
98   int current_depth;
99
100   int ncolors;
101   XColor *colors;
102   int ccolor0;
103   int ccolor1;
104   int ccolor2;
105   int ccolor3;
106
107   int tick;
108
109 } gasketstruct;
110
111 static gasketstruct *gasket = NULL;
112
113
114 static void
115 triangle (GLfloat x1, GLfloat y1, GLfloat z1,
116           GLfloat x2, GLfloat y2, GLfloat z2,
117           GLfloat x3, GLfloat y3, GLfloat z3,
118           Bool wireframe_p)
119 {
120   if (wireframe_p)
121     glBegin (GL_LINE_LOOP);
122   else
123     glBegin (GL_TRIANGLES);
124   glVertex3f (x1, y1, z1);
125   glVertex3f (x2, y2, z2);
126   glVertex3f (x3, y3, z3);
127   glEnd();
128 }
129
130 static void
131 four_tetras (gasketstruct *gp, 
132              XYZ *outer, XYZ *normals,
133              Bool wireframe_p, int countdown, int which,
134              int *countP)
135 {
136   if (countdown <= 0)
137     {
138       (*countP)++;
139       if (which == 0)
140         {
141           glNormal3f (normals[0].x, normals[0].y, normals[0].z);
142           triangle (outer[0].x, outer[0].y, outer[0].z,
143                     outer[1].x, outer[1].y, outer[1].z,
144                     outer[2].x, outer[2].y, outer[2].z,
145                     wireframe_p);
146         }
147       else if (which == 1)
148         {
149           glNormal3f (normals[1].x, normals[1].y, normals[1].z);
150           triangle (outer[0].x, outer[0].y, outer[0].z,
151                     outer[3].x, outer[3].y, outer[3].z,
152                     outer[1].x, outer[1].y, outer[1].z,
153                     wireframe_p);
154         }
155       else if (which == 2)
156         {
157           glNormal3f (normals[2].x, normals[2].y, normals[2].z);
158           triangle (outer[0].x, outer[0].y, outer[0].z,
159                     outer[2].x, outer[2].y, outer[2].z,
160                     outer[3].x, outer[3].y, outer[3].z,
161                     wireframe_p);
162         }
163       else
164         {
165           glNormal3f (normals[3].x, normals[3].y, normals[3].z);
166           triangle (outer[1].x, outer[1].y, outer[1].z,
167                     outer[3].x, outer[3].y, outer[3].z,
168                     outer[2].x, outer[2].y, outer[2].z,
169                     wireframe_p);
170         }
171     }
172   else
173     {
174 #     define M01 0
175 #     define M02 1
176 #     define M03 2
177 #     define M12 3
178 #     define M13 4
179 #     define M23 5
180       XYZ inner[M23+1];
181       XYZ corner[4];
182
183       inner[M01].x = (outer[0].x + outer[1].x) / 2.0;
184       inner[M01].y = (outer[0].y + outer[1].y) / 2.0;
185       inner[M01].z = (outer[0].z + outer[1].z) / 2.0;
186
187       inner[M02].x = (outer[0].x + outer[2].x) / 2.0;
188       inner[M02].y = (outer[0].y + outer[2].y) / 2.0;
189       inner[M02].z = (outer[0].z + outer[2].z) / 2.0;
190
191       inner[M03].x = (outer[0].x + outer[3].x) / 2.0;
192       inner[M03].y = (outer[0].y + outer[3].y) / 2.0;
193       inner[M03].z = (outer[0].z + outer[3].z) / 2.0;
194
195       inner[M12].x = (outer[1].x + outer[2].x) / 2.0;
196       inner[M12].y = (outer[1].y + outer[2].y) / 2.0;
197       inner[M12].z = (outer[1].z + outer[2].z) / 2.0;
198
199       inner[M13].x = (outer[1].x + outer[3].x) / 2.0;
200       inner[M13].y = (outer[1].y + outer[3].y) / 2.0;
201       inner[M13].z = (outer[1].z + outer[3].z) / 2.0;
202
203       inner[M23].x = (outer[2].x + outer[3].x) / 2.0;
204       inner[M23].y = (outer[2].y + outer[3].y) / 2.0;
205       inner[M23].z = (outer[2].z + outer[3].z) / 2.0;
206
207       countdown--;
208
209       corner[0] = outer[0];
210       corner[1] = inner[M01];
211       corner[2] = inner[M02];
212       corner[3] = inner[M03];
213       four_tetras (gp, corner, normals, wireframe_p, countdown, which, countP);
214
215       corner[0] = inner[M01];
216       corner[1] = outer[1];
217       corner[2] = inner[M12];
218       corner[3] = inner[M13];
219       four_tetras (gp, corner, normals, wireframe_p, countdown, which, countP);
220
221       corner[0] = inner[M02];
222       corner[1] = inner[M12];
223       corner[2] = outer[2];
224       corner[3] = inner[M23];
225       four_tetras (gp, corner, normals, wireframe_p, countdown, which, countP);
226
227       corner[0] = inner[M03];
228       corner[1] = inner[M13];
229       corner[2] = inner[M23];
230       corner[3] = outer[3];
231       four_tetras (gp, corner, normals, wireframe_p, countdown, which, countP);
232     }
233 }
234
235
236 static void
237 compile_gasket(ModeInfo *mi, int which)
238 {
239   Bool wireframe_p = MI_IS_WIREFRAME(mi);
240   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
241   int count = 0;
242   XYZ vertex[5];
243   XYZ normal[4];
244
245   vertex[0].x = -1; vertex[0].y = -1; vertex[0].z = -1;
246   vertex[1].x =  1; vertex[1].y =  1; vertex[1].z = -1;
247   vertex[2].x =  1; vertex[2].y = -1; vertex[2].z =  1;
248   vertex[3].x = -1; vertex[3].y =  1; vertex[3].z =  1;
249   vertex[4].x =  0; vertex[4].y =  0; vertex[4].z =  0;  /* center */
250
251   normal[0].x =  1; normal[0].y = -1; normal[0].z = -1;
252   normal[1].x = -1; normal[1].y =  1; normal[1].z = -1;
253   normal[2].x = -1; normal[2].y = -1; normal[2].z =  1;
254   normal[3].x =  1; normal[3].y =  1; normal[3].z =  1;
255
256   four_tetras (gp, vertex, normal, wireframe_p,
257                (gp->current_depth < 0
258                 ? -gp->current_depth : gp->current_depth),
259                which,
260                &count);
261   mi->polygon_count += count;
262 }
263
264 static void
265 draw(ModeInfo *mi)
266 {
267   Bool wireframe_p = MI_IS_WIREFRAME(mi);
268   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
269   
270   static const GLfloat pos[]    = {-4.0, 3.0, 10.0, 1.0};
271   static const GLfloat white[]  = {1.0, 1.0, 1.0, 1.0};
272
273   GLfloat color0[] = {0.0, 0.0, 0.0, 1.0};
274   GLfloat color1[] = {0.0, 0.0, 0.0, 1.0};
275   GLfloat color2[] = {0.0, 0.0, 0.0, 1.0};
276   GLfloat color3[] = {0.0, 0.0, 0.0, 1.0};
277
278   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
279
280   if (!wireframe_p)
281     {
282       glColor4fv (white);
283
284       glLightfv(GL_LIGHT0, GL_POSITION,  pos);
285
286       color0[0] = gp->colors[gp->ccolor0].red   / 65536.0;
287       color0[1] = gp->colors[gp->ccolor0].green / 65536.0;
288       color0[2] = gp->colors[gp->ccolor0].blue  / 65536.0;
289
290       color1[0] = gp->colors[gp->ccolor1].red   / 65536.0;
291       color1[1] = gp->colors[gp->ccolor1].green / 65536.0;
292       color1[2] = gp->colors[gp->ccolor1].blue  / 65536.0;
293
294       color2[0] = gp->colors[gp->ccolor2].red   / 65536.0;
295       color2[1] = gp->colors[gp->ccolor2].green / 65536.0;
296       color2[2] = gp->colors[gp->ccolor2].blue  / 65536.0;
297
298       color3[0] = gp->colors[gp->ccolor3].red   / 65536.0;
299       color3[1] = gp->colors[gp->ccolor3].green / 65536.0;
300       color3[2] = gp->colors[gp->ccolor3].blue  / 65536.0;
301
302       gp->ccolor0++;
303       gp->ccolor1++;
304       gp->ccolor2++;
305       gp->ccolor3++;
306       if (gp->ccolor0 >= gp->ncolors) gp->ccolor0 = 0;
307       if (gp->ccolor1 >= gp->ncolors) gp->ccolor1 = 0;
308       if (gp->ccolor2 >= gp->ncolors) gp->ccolor2 = 0;
309       if (gp->ccolor3 >= gp->ncolors) gp->ccolor3 = 0;
310
311       glShadeModel(GL_SMOOTH);
312
313       glEnable(GL_LIGHTING);
314       glEnable(GL_LIGHT0);
315     }
316
317   glEnable(GL_DEPTH_TEST);
318   glEnable(GL_NORMALIZE);
319   glEnable(GL_CULL_FACE);
320
321   glPushMatrix();
322
323   {
324     double x, y, z;
325     get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
326     glTranslatef((x - 0.5) * 10,
327                  (y - 0.5) * 10,
328                  (z - 0.5) * 20);
329
330     gltrackball_rotate (gp->trackball);
331
332     get_rotation (gp->rot, &x, &y, &z, !gp->button_down_p);
333     glRotatef (x * 360, 1.0, 0.0, 0.0);
334     glRotatef (y * 360, 0.0, 1.0, 0.0);
335     glRotatef (z * 360, 0.0, 0.0, 1.0);
336   }
337
338   glScalef (4, 4, 4);
339
340   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
341   glCallList(gp->gasket0);
342   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
343   glCallList(gp->gasket1);
344   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
345   glCallList(gp->gasket2);
346   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color3);
347   glCallList(gp->gasket3);
348
349   glPopMatrix();
350
351
352   if (gp->tick++ >= speed)
353     {
354       gp->tick = 0;
355       if (gp->current_depth >= max_depth)
356         gp->current_depth = -max_depth;
357       gp->current_depth++;
358
359       /* We make four different lists so that each face of the tetrahedrons
360          can have a different color (all triangles facing in the same
361          direction have the same color, which is different from all
362          triangles facing in other directions.)
363        */
364       glDeleteLists (gp->gasket0, 1);
365       glDeleteLists (gp->gasket1, 1);
366       glDeleteLists (gp->gasket2, 1);
367       glDeleteLists (gp->gasket3, 1);
368
369       mi->polygon_count = 0;
370       glNewList (gp->gasket0, GL_COMPILE); compile_gasket (mi, 0); glEndList();
371       glNewList (gp->gasket1, GL_COMPILE); compile_gasket (mi, 1); glEndList();
372       glNewList (gp->gasket2, GL_COMPILE); compile_gasket (mi, 2); glEndList();
373       glNewList (gp->gasket3, GL_COMPILE); compile_gasket (mi, 3); glEndList();
374
375       mi->recursion_depth = (gp->current_depth > 0
376                              ? gp->current_depth
377                              : -gp->current_depth);
378     }
379 }
380
381
382 /* new window size or exposure */
383 ENTRYPOINT void
384 reshape_gasket(ModeInfo *mi, int width, int height)
385 {
386   GLfloat h = (GLfloat) height / (GLfloat) width;
387
388   glViewport(0, 0, (GLint) width, (GLint) height);
389   glMatrixMode(GL_PROJECTION);
390   glLoadIdentity();
391   gluPerspective (30.0, 1/h, 1.0, 100.0);
392
393   glMatrixMode(GL_MODELVIEW);
394   glLoadIdentity();
395   gluLookAt( 0.0, 0.0, 30.0,
396              0.0, 0.0, 0.0,
397              0.0, 1.0, 0.0);
398   
399   glClear(GL_COLOR_BUFFER_BIT);
400 }
401
402 static void
403 pinit(ModeInfo *mi)
404 {
405   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
406
407   /* draw the gasket */
408   gp->gasket0 = glGenLists(1);
409   gp->gasket1 = glGenLists(1);
410   gp->gasket2 = glGenLists(1);
411   gp->gasket3 = glGenLists(1);
412   gp->current_depth = 1;       /* start out at level 1, not 0 */
413 }
414
415
416 ENTRYPOINT Bool
417 gasket_handle_event (ModeInfo *mi, XEvent *event)
418 {
419   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
420
421   if (gltrackball_event_handler (event, gp->trackball,
422                                  MI_WIDTH (mi), MI_HEIGHT (mi),
423                                  &gp->button_down_p))
424     return True;
425   else if (event->xany.type == KeyPress)
426     {
427       KeySym keysym;
428       char c = 0;
429       XLookupString (&event->xkey, &c, 1, &keysym, 0);
430       if (c == '+' || c == '=' ||
431           keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
432         {
433           gp->tick = speed;
434           gp->current_depth += (gp->current_depth > 0 ? 1 : -1);
435           gp->current_depth--;
436           return True;
437         }
438       else if (c == '-' || c == '_' ||
439                keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
440         {
441           gp->tick = speed;
442           gp->current_depth -= (gp->current_depth > 0 ? 1 : -1);
443           gp->current_depth--;
444           return True;
445         }
446       else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
447         goto DEF;
448     }
449   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
450     {
451     DEF:
452       gp->tick = speed;
453       return True;
454     }
455
456   return False;
457 }
458
459
460 ENTRYPOINT void
461 init_gasket(ModeInfo *mi)
462 {
463   int           screen = MI_SCREEN(mi);
464   gasketstruct *gp;
465
466   if (gasket == NULL)
467   {
468     if ((gasket = (gasketstruct *) calloc(MI_NUM_SCREENS(mi),
469                                               sizeof (gasketstruct))) == NULL)
470         return;
471   }
472   gp = &gasket[screen];
473
474   gp->window = MI_WINDOW(mi);
475
476   {
477     double spin_speed   = 1.0;
478     double wander_speed = 0.03;
479     gp->rot = make_rotator (do_spin ? spin_speed : 0,
480                             do_spin ? spin_speed : 0,
481                             do_spin ? spin_speed : 0,
482                             1.0,
483                             do_wander ? wander_speed : 0,
484                             True);
485     gp->trackball = gltrackball_init (True);
486   }
487
488   gp->ncolors = 255;
489   gp->colors = (XColor *) calloc(gp->ncolors, sizeof(XColor));
490   make_smooth_colormap (0, 0, 0,
491                         gp->colors, &gp->ncolors,
492                         False, 0, False);
493   gp->ccolor0 = 0;
494   gp->ccolor1 = gp->ncolors * 0.25;
495   gp->ccolor2 = gp->ncolors * 0.5;
496   gp->ccolor3 = gp->ncolors * 0.75;
497   gp->tick = 999999;
498
499   if ((gp->glx_context = init_GL(mi)) != NULL)
500   {
501     reshape_gasket(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
502     pinit(mi);
503   }
504   else
505   {
506     MI_CLEARWINDOW(mi);
507   }
508 }
509
510 ENTRYPOINT void
511 draw_gasket(ModeInfo * mi)
512 {
513   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
514   Display      *display = MI_DISPLAY(mi);
515   Window        window = MI_WINDOW(mi);
516
517   if (!gp->glx_context) return;
518
519   glDrawBuffer(GL_BACK);
520
521   /*  0 =              4 polygons
522       1 =             16 polygons
523       2 =             64 polygons
524       3 =            256 polygons
525       4 =          1,024 polygons
526       5 =          4,096 polygons
527       6 =         16,384 polygons
528       7 =         65,536 polygons,  30 fps (3GHz Core 2 Duo, GeForce 8800 GS)
529       8 =        262,144 polygons,  12 fps
530       9 =      1,048,576 polygons,   4 fps
531      10 =      4,194,304 polygons,   1 fps
532      11 =     16,777,216 polygons, 0.3 fps
533      12 =     67,108,864 polygons,    OOM!
534      13 =    268,435,456 polygons
535      14 =  1,073,741,824 polygons, 31 bits
536      15 =  4,294,967,296 polygons, 33 bits
537      16 = 17,179,869,184 polygons, 35 bits
538    */
539   if (max_depth > 10)
540     max_depth = 10;
541
542   glXMakeCurrent(display, window, *(gp->glx_context));
543   draw(mi);
544   if (mi->fps_p) do_fps (mi);
545   glFinish();
546   glXSwapBuffers(display, window);
547 }
548
549 ENTRYPOINT void
550 release_gasket(ModeInfo * mi)
551 {
552   if (gasket != NULL)
553   {
554     int         screen;
555
556     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
557     {
558       gasketstruct *gp = &gasket[screen];
559
560       if (gp->glx_context)
561       {
562         /* Display lists MUST be freed while their glXContext is current. */
563         glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
564
565         if (glIsList(gp->gasket0)) glDeleteLists(gp->gasket0, 1);
566         if (glIsList(gp->gasket1)) glDeleteLists(gp->gasket1, 1);
567         if (glIsList(gp->gasket2)) glDeleteLists(gp->gasket2, 1);
568         if (glIsList(gp->gasket3)) glDeleteLists(gp->gasket3, 1);
569       }
570     }
571     (void) free((void *) gasket);
572     gasket = NULL;
573   }
574   FreeAllGL(mi);
575 }
576
577 XSCREENSAVER_MODULE_2 ("Sierpinski3D", sierpinski3d, gasket)
578
579 /*********************************************************/
580
581 #endif