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