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