b061bf23f47f910dff90167be11b7cfccb9ed574
[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 /*-
30  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
31  * otherwise caddr_t is not defined correctly
32  */
33
34 #include <X11/Intrinsic.h>
35
36 #ifdef STANDALONE
37 # define PROGCLASS                                      "Sierpinski3D"
38 # define HACK_INIT                                      init_gasket
39 # define HACK_DRAW                                      draw_gasket
40 # define HACK_RESHAPE                           reshape_gasket
41 # define HACK_HANDLE_EVENT                      gasket_handle_event
42 # define EVENT_MASK                                     PointerMotionMask
43 # define gasket_opts                            xlockmore_opts
44
45
46 #define DEF_SPIN                                "True"
47 #define DEF_WANDER                              "True"
48 #define DEF_SPEED                               "150"
49 #define DEF_MAXDEPTH                    "5"
50
51 # define DEFAULTS                                       "*delay:                20000   \n"                     \
52                                                                         "*showFPS:      False   \n"                     \
53                                                                         "*wireframe:    False   \n"                     \
54                                                                         "*maxDepth:        " DEF_MAXDEPTH  "\n" \
55                                                                         "*speed:           " DEF_SPEED     "\n" \
56                                                                         "*spin:        " DEF_SPIN      "\n" \
57                                                                         "*wander:      " DEF_WANDER    "\n" \
58
59 # include "xlockmore.h"         /* from the xscreensaver distribution */
60 #else  /* !STANDALONE */
61 # include "xlock.h"                     /* from the xlockmore distribution */
62 #endif /* !STANDALONE */
63
64 #ifdef USE_GL
65
66 #include <GL/glu.h>
67 #include "rotator.h"
68 #include "gltrackball.h"
69
70 #undef countof
71 #define countof(x) (sizeof((x))/sizeof((*x)))
72
73 static int max_depth;
74 static int speed;
75 static Bool do_spin;
76 static Bool do_wander;
77
78 static XrmOptionDescRec opts[] = {
79   {"-depth", ".sierpinski3d.maxDepth", XrmoptionSepArg, (caddr_t) 0 },
80   {"-speed", ".sierpinski3d.speed",    XrmoptionSepArg, (caddr_t) 0 },
81   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
82   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
83   { "-wander", ".wander", XrmoptionNoArg, "True" },
84   { "+wander", ".wander", XrmoptionNoArg, "False" },
85 };
86
87 static argtype vars[] = {
88   {&do_spin,   "spin",     "Spin",     DEF_SPIN,     t_Bool},
89   {&do_wander, "wander",   "Wander",   DEF_WANDER,   t_Bool},
90   {&speed,     "speed",    "Speed",    DEF_SPEED,    t_Int},
91   {&max_depth, "maxDepth", "MaxDepth", DEF_MAXDEPTH, t_Int},
92 };
93
94
95 ModeSpecOpt gasket_opts = {countof(opts), opts, countof(vars), vars, NULL};
96
97 #ifdef USE_MODULES
98 ModStruct   gasket_description =
99 {"gasket", "init_gasket", "draw_gasket", "release_gasket",
100  "draw_gasket", "init_gasket", NULL, &gasket_opts,
101  1000, 1, 2, 1, 4, 1.0, "",
102  "Shows GL's Sierpinski gasket", 0, NULL};
103
104 #endif
105
106 typedef struct{
107   GLfloat x;
108   GLfloat y;
109   GLfloat z;
110 } GL_VECTOR;
111
112 typedef struct {
113   GLuint      gasket0, gasket1, gasket2, gasket3;
114   GLXContext *glx_context;
115   Window      window;
116   rotator    *rot;
117   trackball_state *trackball;
118   Bool            button_down_p;
119
120   int current_depth;
121
122   int ncolors;
123   XColor *colors;
124   int ccolor0;
125   int ccolor1;
126   int ccolor2;
127   int ccolor3;
128
129 } gasketstruct;
130
131 static gasketstruct *gasket = NULL;
132
133 static GLfloat normals[4][3];
134
135 \f
136
137 static void
138 triangle (GLfloat x1, GLfloat y1, GLfloat z1,
139           GLfloat x2, GLfloat y2, GLfloat z2,
140           GLfloat x3, GLfloat y3, GLfloat z3,
141           Bool wireframe_p)
142 {
143   if (wireframe_p)
144     glBegin (GL_LINE_LOOP);
145   else
146     glBegin (GL_TRIANGLES);
147   glVertex3f (x1, y1, z1);
148   glVertex3f (x2, y2, z2);
149   glVertex3f (x3, y3, z3);
150   glEnd();
151 }
152
153 static void
154 four_tetras (GL_VECTOR *outer, Bool wireframe_p, int countdown, int which,
155              int *countP)
156 {
157   if (countdown <= 0)
158     {
159       (*countP)++;
160       if (which == 0)
161         {
162           glNormal3f (normals[0][0], normals[0][1], normals[0][2]);
163           triangle (outer[0].x, outer[0].y, outer[0].z,
164                     outer[1].x, outer[1].y, outer[1].z,
165                     outer[2].x, outer[2].y, outer[2].z,
166                     wireframe_p);
167         }
168       else if (which == 1)
169         {
170           glNormal3f (normals[1][0], normals[1][1], normals[1][2]);
171           triangle (outer[0].x, outer[0].y, outer[0].z,
172                     outer[3].x, outer[3].y, outer[3].z,
173                     outer[1].x, outer[1].y, outer[1].z,
174                     wireframe_p);
175         }
176       else if (which == 2)
177         {
178           glNormal3f (normals[2][0], normals[2][1], normals[2][2]);
179           triangle (outer[0].x, outer[0].y, outer[0].z,
180                     outer[2].x, outer[2].y, outer[2].z,
181                     outer[3].x, outer[3].y, outer[3].z,
182                     wireframe_p);
183         }
184       else
185         {
186           glNormal3f (normals[3][0], normals[3][1], normals[3][2]);
187           triangle (outer[1].x, outer[1].y, outer[1].z,
188                     outer[3].x, outer[3].y, outer[3].z,
189                     outer[2].x, outer[2].y, outer[2].z,
190                     wireframe_p);
191         }
192     }
193   else
194     {
195 #     define M01 0
196 #     define M02 1
197 #     define M03 2
198 #     define M12 3
199 #     define M13 4
200 #     define M23 5
201       GL_VECTOR inner[M23+1];
202       GL_VECTOR corner[4];
203
204       inner[M01].x = (outer[0].x + outer[1].x) / 2.0;
205       inner[M01].y = (outer[0].y + outer[1].y) / 2.0;
206       inner[M01].z = (outer[0].z + outer[1].z) / 2.0;
207
208       inner[M02].x = (outer[0].x + outer[2].x) / 2.0;
209       inner[M02].y = (outer[0].y + outer[2].y) / 2.0;
210       inner[M02].z = (outer[0].z + outer[2].z) / 2.0;
211
212       inner[M03].x = (outer[0].x + outer[3].x) / 2.0;
213       inner[M03].y = (outer[0].y + outer[3].y) / 2.0;
214       inner[M03].z = (outer[0].z + outer[3].z) / 2.0;
215
216       inner[M12].x = (outer[1].x + outer[2].x) / 2.0;
217       inner[M12].y = (outer[1].y + outer[2].y) / 2.0;
218       inner[M12].z = (outer[1].z + outer[2].z) / 2.0;
219
220       inner[M13].x = (outer[1].x + outer[3].x) / 2.0;
221       inner[M13].y = (outer[1].y + outer[3].y) / 2.0;
222       inner[M13].z = (outer[1].z + outer[3].z) / 2.0;
223
224       inner[M23].x = (outer[2].x + outer[3].x) / 2.0;
225       inner[M23].y = (outer[2].y + outer[3].y) / 2.0;
226       inner[M23].z = (outer[2].z + outer[3].z) / 2.0;
227
228       countdown--;
229
230       corner[0] = outer[0];
231       corner[1] = inner[M01];
232       corner[2] = inner[M02];
233       corner[3] = inner[M03];
234       four_tetras (corner, wireframe_p, countdown, which, countP);
235
236       corner[0] = inner[M01];
237       corner[1] = outer[1];
238       corner[2] = inner[M12];
239       corner[3] = inner[M13];
240       four_tetras (corner, wireframe_p, countdown, which, countP);
241
242       corner[0] = inner[M02];
243       corner[1] = inner[M12];
244       corner[2] = outer[2];
245       corner[3] = inner[M23];
246       four_tetras (corner, wireframe_p, countdown, which, countP);
247
248       corner[0] = inner[M03];
249       corner[1] = inner[M13];
250       corner[2] = inner[M23];
251       corner[3] = outer[3];
252       four_tetras (corner, wireframe_p, countdown, which, countP);
253     }
254 }
255
256
257 static void
258 compile_gasket(ModeInfo *mi, int which)
259 {
260   Bool wireframe_p = MI_IS_WIREFRAME(mi);
261   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
262   int count;
263
264   GL_VECTOR   vertex[5];
265
266   normals[0][0] =  0;
267   normals[0][1] =  0;
268   normals[0][2] = -sqrt(2.0 / 3.0);
269
270   normals[1][0] =  0;
271   normals[1][1] = -sqrt(0.75);
272   normals[1][2] =  sqrt(2.0 / 3.0) / 3.0;
273
274   normals[2][0] =  sqrt (0.5);
275   normals[2][1] =  sqrt(0.75) / 2.0;
276   normals[2][2] =  normals[1][2];
277
278   normals[3][0] = -normals[2][0];
279   normals[3][1] =  normals[2][1];
280   normals[3][2] =  normals[1][2];
281
282
283   /* define verticies */
284   vertex[0].x =  0.5; 
285   vertex[0].y = -(1.0/3.0)*sqrt((2.0/3.0));
286   vertex[0].z = -sqrt(3.0)/6.0;
287
288   vertex[1].x = -0.5; 
289   vertex[1].y = -(1.0/3.0)*sqrt((2.0/3.0)); 
290   vertex[1].z = -sqrt(3.0)/6.0; 
291
292   vertex[2].x = 0.0; 
293   vertex[2].y = (2.0/3.0)*sqrt((2.0/3.0));
294   vertex[2].z = -sqrt(3.0)/6.0; 
295
296   vertex[3].x = 0.0; 
297   vertex[3].y = 0.0; 
298   vertex[3].z = sqrt(3.0)/3.0; 
299
300   vertex[4].x = 0.0;
301   vertex[4].y = 0.0; 
302   vertex[4].z = 0.0;
303   
304   count = 0;
305   four_tetras (vertex, wireframe_p,
306                (gp->current_depth < 0
307                 ? -gp->current_depth : gp->current_depth),
308                which,
309                &count);
310   mi->polygon_count = count;
311 }
312
313 static void
314 draw(ModeInfo *mi)
315 {
316   Bool wireframe_p = MI_IS_WIREFRAME(mi);
317   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
318   static int tick = 999999;
319   
320   static GLfloat pos[4] = {-4.0, 3.0, 10.0, 1.0};
321   static float white[]  = {1.0, 1.0, 1.0, 1.0};
322   static float color0[] = {0.0, 0.0, 0.0, 1.0};
323   static float color1[] = {0.0, 0.0, 0.0, 1.0};
324   static float color2[] = {0.0, 0.0, 0.0, 1.0};
325   static float color3[] = {0.0, 0.0, 0.0, 1.0};
326
327   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
328
329   if (!wireframe_p)
330     {
331       glColor4fv (white);
332
333       glLightfv(GL_LIGHT0, GL_POSITION,  pos);
334
335       color0[0] = gp->colors[gp->ccolor0].red   / 65536.0;
336       color0[1] = gp->colors[gp->ccolor0].green / 65536.0;
337       color0[2] = gp->colors[gp->ccolor0].blue  / 65536.0;
338
339       color1[0] = gp->colors[gp->ccolor1].red   / 65536.0;
340       color1[1] = gp->colors[gp->ccolor1].green / 65536.0;
341       color1[2] = gp->colors[gp->ccolor1].blue  / 65536.0;
342
343       color2[0] = gp->colors[gp->ccolor2].red   / 65536.0;
344       color2[1] = gp->colors[gp->ccolor2].green / 65536.0;
345       color2[2] = gp->colors[gp->ccolor2].blue  / 65536.0;
346
347       color3[0] = gp->colors[gp->ccolor3].red   / 65536.0;
348       color3[1] = gp->colors[gp->ccolor3].green / 65536.0;
349       color3[2] = gp->colors[gp->ccolor3].blue  / 65536.0;
350
351       gp->ccolor0++;
352       gp->ccolor1++;
353       gp->ccolor2++;
354       gp->ccolor3++;
355       if (gp->ccolor0 >= gp->ncolors) gp->ccolor0 = 0;
356       if (gp->ccolor1 >= gp->ncolors) gp->ccolor1 = 0;
357       if (gp->ccolor2 >= gp->ncolors) gp->ccolor2 = 0;
358       if (gp->ccolor3 >= gp->ncolors) gp->ccolor3 = 0;
359
360       glShadeModel(GL_SMOOTH);
361
362       glEnable(GL_LIGHTING);
363       glEnable(GL_LIGHT0);
364     }
365
366   glEnable(GL_DEPTH_TEST);
367   glEnable(GL_NORMALIZE);
368   glEnable(GL_CULL_FACE);
369
370   glPushMatrix();
371
372   {
373     double x, y, z;
374     get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
375     glTranslatef((x - 0.5) * 10,
376                  (y - 0.5) * 10,
377                  (z - 0.5) * 20);
378
379     gltrackball_rotate (gp->trackball);
380
381     get_rotation (gp->rot, &x, &y, &z, !gp->button_down_p);
382     glRotatef (x * 360, 1.0, 0.0, 0.0);
383     glRotatef (y * 360, 0.0, 1.0, 0.0);
384     glRotatef (z * 360, 0.0, 0.0, 1.0);
385   }
386
387   glScalef( 8.0, 8.0, 8.0 );
388
389   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
390   glCallList(gp->gasket0);
391   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
392   glCallList(gp->gasket1);
393   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
394   glCallList(gp->gasket2);
395   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color3);
396   glCallList(gp->gasket3);
397
398   glPopMatrix();
399
400
401   if (tick++ >= speed)
402     {
403       tick = 0;
404       if (gp->current_depth >= max_depth)
405         gp->current_depth = -max_depth;
406       gp->current_depth++;
407
408       /* We make four different lists so that each face of the tetrahedrons
409          can have a different color (all triangles facing in the same
410          direction have the same color, which is different from all
411          triangles facing in other directions.)
412        */
413       glDeleteLists (gp->gasket0, 1);
414       glDeleteLists (gp->gasket1, 1);
415       glDeleteLists (gp->gasket2, 1);
416       glDeleteLists (gp->gasket3, 1);
417
418       mi->polygon_count = 0;
419       glNewList (gp->gasket0, GL_COMPILE); compile_gasket (mi, 0); glEndList();
420       glNewList (gp->gasket1, GL_COMPILE); compile_gasket (mi, 1); glEndList();
421       glNewList (gp->gasket2, GL_COMPILE); compile_gasket (mi, 2); glEndList();
422       glNewList (gp->gasket3, GL_COMPILE); compile_gasket (mi, 3); glEndList();
423
424     }
425 }
426
427
428 /* new window size or exposure */
429 void
430 reshape_gasket(ModeInfo *mi, int width, int height)
431 {
432   GLfloat h = (GLfloat) height / (GLfloat) width;
433
434   glViewport(0, 0, (GLint) width, (GLint) height);
435   glMatrixMode(GL_PROJECTION);
436   glLoadIdentity();
437   gluPerspective (30.0, 1/h, 1.0, 100.0);
438
439   glMatrixMode(GL_MODELVIEW);
440   glLoadIdentity();
441   gluLookAt( 0.0, 0.0, 30.0,
442              0.0, 0.0, 0.0,
443              0.0, 1.0, 0.0);
444   
445   glClear(GL_COLOR_BUFFER_BIT);
446 }
447
448 static void
449 pinit(ModeInfo *mi)
450 {
451   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
452
453   /* draw the gasket */
454   gp->gasket0 = glGenLists(1);
455   gp->gasket1 = glGenLists(1);
456   gp->gasket2 = glGenLists(1);
457   gp->gasket3 = glGenLists(1);
458   gp->current_depth = 1;       /* start out at level 1, not 0 */
459 }
460
461
462 Bool
463 gasket_handle_event (ModeInfo *mi, XEvent *event)
464 {
465   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
466
467   if (event->xany.type == ButtonPress &&
468       event->xbutton.button & Button1)
469     {
470       gp->button_down_p = True;
471       gltrackball_start (gp->trackball,
472                          event->xbutton.x, event->xbutton.y,
473                          MI_WIDTH (mi), MI_HEIGHT (mi));
474       return True;
475     }
476   else if (event->xany.type == ButtonRelease &&
477            event->xbutton.button & Button1)
478     {
479       gp->button_down_p = False;
480       return True;
481     }
482   else if (event->xany.type == MotionNotify &&
483            gp->button_down_p)
484     {
485       gltrackball_track (gp->trackball,
486                          event->xmotion.x, event->xmotion.y,
487                          MI_WIDTH (mi), MI_HEIGHT (mi));
488       return True;
489     }
490
491   return False;
492 }
493
494
495 void
496 init_gasket(ModeInfo *mi)
497 {
498   int           screen = MI_SCREEN(mi);
499   gasketstruct *gp;
500
501   if (gasket == NULL)
502   {
503     if ((gasket = (gasketstruct *) calloc(MI_NUM_SCREENS(mi),
504                                               sizeof (gasketstruct))) == NULL)
505         return;
506   }
507   gp = &gasket[screen];
508
509   gp->window = MI_WINDOW(mi);
510
511   {
512     double spin_speed   = 1.0;
513     double wander_speed = 0.03;
514     gp->rot = make_rotator (do_spin ? spin_speed : 0,
515                             do_spin ? spin_speed : 0,
516                             do_spin ? spin_speed : 0,
517                             1.0,
518                             do_wander ? wander_speed : 0,
519                             True);
520     gp->trackball = gltrackball_init ();
521   }
522
523   gp->ncolors = 255;
524   gp->colors = (XColor *) calloc(gp->ncolors, sizeof(XColor));
525   make_smooth_colormap (0, 0, 0,
526                         gp->colors, &gp->ncolors,
527                         False, 0, False);
528   gp->ccolor0 = 0;
529   gp->ccolor1 = gp->ncolors * 0.25;
530   gp->ccolor2 = gp->ncolors * 0.5;
531   gp->ccolor3 = gp->ncolors * 0.75;
532
533   if ((gp->glx_context = init_GL(mi)) != NULL)
534   {
535     reshape_gasket(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
536     pinit(mi);
537   }
538   else
539   {
540     MI_CLEARWINDOW(mi);
541   }
542 }
543
544 void
545 draw_gasket(ModeInfo * mi)
546 {
547   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
548   Display      *display = MI_DISPLAY(mi);
549   Window        window = MI_WINDOW(mi);
550
551   if (!gp->glx_context) return;
552
553   glDrawBuffer(GL_BACK);
554
555   if (max_depth > 10)
556     max_depth = 10;
557
558   glXMakeCurrent(display, window, *(gp->glx_context));
559   draw(mi);
560   if (mi->fps_p) do_fps (mi);
561   glFinish();
562   glXSwapBuffers(display, window);
563 }
564
565 void
566 release_gasket(ModeInfo * mi)
567 {
568   if (gasket != NULL)
569   {
570     int         screen;
571
572     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
573     {
574       gasketstruct *gp = &gasket[screen];
575
576       if (gp->glx_context)
577       {
578         /* Display lists MUST be freed while their glXContext is current. */
579         glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
580
581         if (glIsList(gp->gasket0)) glDeleteLists(gp->gasket0, 1);
582         if (glIsList(gp->gasket1)) glDeleteLists(gp->gasket1, 1);
583         if (glIsList(gp->gasket2)) glDeleteLists(gp->gasket2, 1);
584         if (glIsList(gp->gasket3)) glDeleteLists(gp->gasket3, 1);
585       }
586     }
587     (void) free((void *) gasket);
588     gasket = NULL;
589   }
590   FreeAllGL(mi);
591 }
592
593
594 /*********************************************************/
595
596 #endif