From http://www.jwz.org/xscreensaver/xscreensaver-5.18.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     /* Do it twice because we don't track the device's orientation. */
331     glRotatef( current_device_rotation(), 0, 0, 1);
332     gltrackball_rotate (gp->trackball);
333     glRotatef(-current_device_rotation(), 0, 0, 1);
334
335     get_rotation (gp->rot, &x, &y, &z, !gp->button_down_p);
336     glRotatef (x * 360, 1.0, 0.0, 0.0);
337     glRotatef (y * 360, 0.0, 1.0, 0.0);
338     glRotatef (z * 360, 0.0, 0.0, 1.0);
339   }
340
341   glScalef (4, 4, 4);
342
343   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
344   glCallList(gp->gasket0);
345   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
346   glCallList(gp->gasket1);
347   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
348   glCallList(gp->gasket2);
349   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color3);
350   glCallList(gp->gasket3);
351
352   glPopMatrix();
353
354
355   if (gp->tick++ >= speed)
356     {
357       gp->tick = 0;
358       if (gp->current_depth >= max_depth)
359         gp->current_depth = -max_depth;
360       gp->current_depth++;
361
362       /* We make four different lists so that each face of the tetrahedrons
363          can have a different color (all triangles facing in the same
364          direction have the same color, which is different from all
365          triangles facing in other directions.)
366        */
367       glDeleteLists (gp->gasket0, 1);
368       glDeleteLists (gp->gasket1, 1);
369       glDeleteLists (gp->gasket2, 1);
370       glDeleteLists (gp->gasket3, 1);
371
372       mi->polygon_count = 0;
373       glNewList (gp->gasket0, GL_COMPILE); compile_gasket (mi, 0); glEndList();
374       glNewList (gp->gasket1, GL_COMPILE); compile_gasket (mi, 1); glEndList();
375       glNewList (gp->gasket2, GL_COMPILE); compile_gasket (mi, 2); glEndList();
376       glNewList (gp->gasket3, GL_COMPILE); compile_gasket (mi, 3); glEndList();
377
378       mi->recursion_depth = (gp->current_depth > 0
379                              ? gp->current_depth
380                              : -gp->current_depth);
381     }
382 }
383
384
385 /* new window size or exposure */
386 ENTRYPOINT void
387 reshape_gasket(ModeInfo *mi, int width, int height)
388 {
389   GLfloat h = (GLfloat) height / (GLfloat) width;
390
391   glViewport(0, 0, (GLint) width, (GLint) height);
392   glMatrixMode(GL_PROJECTION);
393   glLoadIdentity();
394   gluPerspective (30.0, 1/h, 1.0, 100.0);
395
396   glMatrixMode(GL_MODELVIEW);
397   glLoadIdentity();
398   gluLookAt( 0.0, 0.0, 30.0,
399              0.0, 0.0, 0.0,
400              0.0, 1.0, 0.0);
401   
402   glClear(GL_COLOR_BUFFER_BIT);
403 }
404
405 static void
406 pinit(ModeInfo *mi)
407 {
408   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
409
410   /* draw the gasket */
411   gp->gasket0 = glGenLists(1);
412   gp->gasket1 = glGenLists(1);
413   gp->gasket2 = glGenLists(1);
414   gp->gasket3 = glGenLists(1);
415   gp->current_depth = 1;       /* start out at level 1, not 0 */
416 }
417
418
419 ENTRYPOINT Bool
420 gasket_handle_event (ModeInfo *mi, XEvent *event)
421 {
422   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
423
424   if (event->xany.type == ButtonPress &&
425       event->xbutton.button == Button1)
426     {
427       gp->button_down_p = True;
428       gltrackball_start (gp->trackball,
429                          event->xbutton.x, event->xbutton.y,
430                          MI_WIDTH (mi), MI_HEIGHT (mi));
431       return True;
432     }
433   else if (event->xany.type == ButtonRelease &&
434            event->xbutton.button == Button1)
435     {
436       gp->button_down_p = False;
437       return True;
438     }
439   else if (event->xany.type == ButtonPress &&
440            (event->xbutton.button == Button4 ||
441             event->xbutton.button == Button5 ||
442             event->xbutton.button == Button6 ||
443             event->xbutton.button == Button7))
444     {
445       gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
446                               !!event->xbutton.state);
447       return True;
448     }
449   else if (event->xany.type == MotionNotify &&
450            gp->button_down_p)
451     {
452       gltrackball_track (gp->trackball,
453                          event->xmotion.x, event->xmotion.y,
454                          MI_WIDTH (mi), MI_HEIGHT (mi));
455       return True;
456     }
457
458   return False;
459 }
460
461
462 ENTRYPOINT void
463 init_gasket(ModeInfo *mi)
464 {
465   int           screen = MI_SCREEN(mi);
466   gasketstruct *gp;
467
468   if (gasket == NULL)
469   {
470     if ((gasket = (gasketstruct *) calloc(MI_NUM_SCREENS(mi),
471                                               sizeof (gasketstruct))) == NULL)
472         return;
473   }
474   gp = &gasket[screen];
475
476   gp->window = MI_WINDOW(mi);
477
478   {
479     double spin_speed   = 1.0;
480     double wander_speed = 0.03;
481     gp->rot = make_rotator (do_spin ? spin_speed : 0,
482                             do_spin ? spin_speed : 0,
483                             do_spin ? spin_speed : 0,
484                             1.0,
485                             do_wander ? wander_speed : 0,
486                             True);
487     gp->trackball = gltrackball_init ();
488   }
489
490   gp->ncolors = 255;
491   gp->colors = (XColor *) calloc(gp->ncolors, sizeof(XColor));
492   make_smooth_colormap (0, 0, 0,
493                         gp->colors, &gp->ncolors,
494                         False, 0, False);
495   gp->ccolor0 = 0;
496   gp->ccolor1 = gp->ncolors * 0.25;
497   gp->ccolor2 = gp->ncolors * 0.5;
498   gp->ccolor3 = gp->ncolors * 0.75;
499   gp->tick = 999999;
500
501   if ((gp->glx_context = init_GL(mi)) != NULL)
502   {
503     reshape_gasket(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
504     pinit(mi);
505   }
506   else
507   {
508     MI_CLEARWINDOW(mi);
509   }
510 }
511
512 ENTRYPOINT void
513 draw_gasket(ModeInfo * mi)
514 {
515   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
516   Display      *display = MI_DISPLAY(mi);
517   Window        window = MI_WINDOW(mi);
518
519   if (!gp->glx_context) return;
520
521   glDrawBuffer(GL_BACK);
522
523   /*  0 =              4 polygons
524       1 =             16 polygons
525       2 =             64 polygons
526       3 =            256 polygons
527       4 =          1,024 polygons
528       5 =          4,096 polygons
529       6 =         16,384 polygons
530       7 =         65,536 polygons,  30 fps (3GHz Core 2 Duo, GeForce 8800 GS)
531       8 =        262,144 polygons,  12 fps
532       9 =      1,048,576 polygons,   4 fps
533      10 =      4,194,304 polygons,   1 fps
534      11 =     16,777,216 polygons, 0.3 fps
535      12 =     67,108,864 polygons,    OOM!
536      13 =    268,435,456 polygons
537      14 =  1,073,741,824 polygons, 31 bits
538      15 =  4,294,967,296 polygons, 33 bits
539      16 = 17,179,869,184 polygons, 35 bits
540    */
541   if (max_depth > 10)
542     max_depth = 10;
543
544   glXMakeCurrent(display, window, *(gp->glx_context));
545   draw(mi);
546   if (mi->fps_p) do_fps (mi);
547   glFinish();
548   glXSwapBuffers(display, window);
549 }
550
551 ENTRYPOINT void
552 release_gasket(ModeInfo * mi)
553 {
554   if (gasket != NULL)
555   {
556     int         screen;
557
558     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
559     {
560       gasketstruct *gp = &gasket[screen];
561
562       if (gp->glx_context)
563       {
564         /* Display lists MUST be freed while their glXContext is current. */
565         glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
566
567         if (glIsList(gp->gasket0)) glDeleteLists(gp->gasket0, 1);
568         if (glIsList(gp->gasket1)) glDeleteLists(gp->gasket1, 1);
569         if (glIsList(gp->gasket2)) glDeleteLists(gp->gasket2, 1);
570         if (glIsList(gp->gasket3)) glDeleteLists(gp->gasket3, 1);
571       }
572     }
573     (void) free((void *) gasket);
574     gasket = NULL;
575   }
576   FreeAllGL(mi);
577 }
578
579 XSCREENSAVER_MODULE_2 ("Sierpinski3D", sierpinski3d, gasket)
580
581 /*********************************************************/
582
583 #endif