d11daf1aeed98a98498b861af1262f56744baff5
[xscreensaver] / hacks / glx / sierpinski3d.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* Sierpinski3D --- 3D sierpinski gasket */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)sierpinski3D.c        00.01 99/11/04 xlockmore";
6
7 #endif
8
9 /*-
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Revision History:
23  * 1999: written by Tim Robinson <the_luggage@bigfoot.com>
24  *       a 3-D representation of the Sierpinski gasket fractal.
25  *
26  * 10-Dec-99  jwz   rewrote to draw a set of tetrahedrons instead of a
27  *                  random scattering of points.
28  */
29
30 /*-
31  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
32  * otherwise caddr_t is not defined correctly
33  */
34
35 #include <X11/Intrinsic.h>
36
37 #ifdef STANDALONE
38 # define PROGCLASS                                      "Sierpinski3D"
39 # define HACK_INIT                                      init_gasket
40 # define HACK_DRAW                                      draw_gasket
41 # define gasket_opts                            xlockmore_opts
42 # define DEFAULTS       "*count:                1       \n"                     \
43                         "*cycles:               9999    \n"                     \
44                         "*delay:                8000    \n"                     \
45                         "*maxDepth:             5       \n"                     \
46                         "*speed:                150     \n"                     \
47                         "*wireframe:    False   \n"
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 #undef countof
56 #define countof(x) (sizeof((x))/sizeof((*x)))
57
58 static int max_depth;
59 static int speed;
60 static XrmOptionDescRec opts[] = {
61   {"-depth", ".sierpinski3d.maxDepth", XrmoptionSepArg, (caddr_t) 0 },
62   {"-speed", ".sierpinski3d.speed",    XrmoptionSepArg, (caddr_t) 0 }
63 };
64
65 static argtype vars[] = {
66   {(caddr_t *) &max_depth, "maxDepth", "MaxDepth", "5", t_Int},
67   {(caddr_t *) &speed,     "speed",    "Speed",   "150", t_Int},
68 };
69
70
71 ModeSpecOpt gasket_opts = {countof(opts), opts, countof(vars), vars, NULL};
72
73 #ifdef USE_MODULES
74 ModStruct   gasket_description =
75 {"gasket", "init_gasket", "draw_gasket", "release_gasket",
76  "draw_gasket", "init_gasket", NULL, &gasket_opts,
77  1000, 1, 2, 1, 4, 1.0, "",
78  "Shows GL's Sierpinski gasket", 0, NULL};
79
80 #endif
81
82 typedef struct{
83   GLfloat x;
84   GLfloat y;
85   GLfloat z;
86 } GL_VECTOR;
87
88 typedef struct {
89   GLfloat     view_rotx, view_roty, view_rotz;
90   GLfloat     light_colour[4];/* = {6.0, 6.0, 6.0, 1.0}; */
91   GLfloat     pos[3];/* = {0.0, 0.0, 0.0}; */
92   GLfloat     xinc,yinc,zinc;
93   GLfloat     angle;
94   GLuint      gasket1;
95   GLXContext *glx_context;
96   Window      window;
97
98   int current_depth;
99
100 } gasketstruct;
101
102 static gasketstruct *gasket = NULL;
103
104 #include <GL/glu.h>
105
106 /* static GLuint limit; */
107
108 \f
109 /* Computing normal vectors (thanks to Nat Friedman <ndf@mit.edu>)
110  */
111
112 typedef struct vector {
113   GLfloat x, y, z;
114 } vector;
115
116 typedef struct plane {
117   vector p1, p2, p3;
118 } plane;
119
120 static void
121 vector_set(vector *v, GLfloat x, GLfloat y, GLfloat z)
122 {
123   v->x = x;
124   v->y = y;
125   v->z = z;
126 }
127
128 static void
129 vector_cross(vector v1, vector v2, vector *v3)
130 {
131   v3->x = (v1.y * v2.z) - (v1.z * v2.y);
132   v3->y = (v1.z * v2.x) - (v1.x * v2.z);
133   v3->z = (v1.x * v2.y) - (v1.y * v2.x);
134 }
135
136 static void
137 vector_subtract(vector v1, vector v2, vector *res)
138 {
139   res->x = v1.x - v2.x;
140   res->y = v1.y - v2.y;
141   res->z = v1.z - v2.z;
142 }
143
144 static void
145 plane_normal(plane p, vector *n)
146 {
147   vector v1, v2;
148   vector_subtract(p.p1, p.p2, &v1);
149   vector_subtract(p.p1, p.p3, &v2);
150   vector_cross(v2, v1, n);
151 }
152
153 static void
154 do_normal(GLfloat x1, GLfloat y1, GLfloat z1,
155           GLfloat x2, GLfloat y2, GLfloat z2,
156           GLfloat x3, GLfloat y3, GLfloat z3)
157 {
158   plane plane;
159   vector n;
160   vector_set(&plane.p1, x1, y1, z1);
161   vector_set(&plane.p2, x2, y2, z2);
162   vector_set(&plane.p3, x3, y3, z3);
163   plane_normal(plane, &n);
164   n.x = -n.x; n.y = -n.y; n.z = -n.z;
165
166   glNormal3f(n.x, n.y, n.z);
167
168 #ifdef DEBUG
169   /* Draw a line in the direction of this face's normal. */
170   {
171     GLfloat ax = n.x > 0 ? n.x : -n.x;
172     GLfloat ay = n.y > 0 ? n.y : -n.y;
173     GLfloat az = n.z > 0 ? n.z : -n.z;
174     GLfloat mx = (x1 + x2 + x3) / 3;
175     GLfloat my = (y1 + y2 + y3) / 3;
176     GLfloat mz = (z1 + z2 + z3) / 3;
177     GLfloat xx, yy, zz;
178
179     GLfloat max = ax > ay ? ax : ay;
180     if (az > max) max = az;
181     max *= 2;
182     xx = n.x / max;
183     yy = n.y / max;
184     zz = n.z / max;
185
186     glBegin(GL_LINE_LOOP);
187     glVertex3f(mx, my, mz);
188     glVertex3f(mx+xx, my+yy, mz+zz);
189     glEnd();
190   }
191 #endif /* DEBUG */
192 }
193
194 \f
195
196 static void
197 triangle (GLfloat x1, GLfloat y1, GLfloat z1,
198           GLfloat x2, GLfloat y2, GLfloat z2,
199           GLfloat x3, GLfloat y3, GLfloat z3,
200           Bool wireframe_p)
201 {
202   if (wireframe_p)
203     glBegin (GL_LINE_LOOP);
204   else
205     {
206       do_normal (x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
207       glBegin (GL_TRIANGLES);
208     }
209   glVertex3f (x1, y1, z1);
210   glVertex3f (x2, y2, z2);
211   glVertex3f (x3, y3, z3);
212   glEnd();
213 }
214
215 static void
216 four_tetras (GL_VECTOR *outer, Bool wireframe_p, int countdown)
217 {
218   if (countdown <= 0)
219     {
220       triangle (outer[0].x, outer[0].y, outer[0].z,
221                 outer[1].x, outer[1].y, outer[1].z,
222                 outer[2].x, outer[2].y, outer[2].z,
223                 wireframe_p);
224       triangle (outer[0].x, outer[0].y, outer[0].z,
225                 outer[3].x, outer[3].y, outer[3].z,
226                 outer[1].x, outer[1].y, outer[1].z,
227                 wireframe_p);
228       triangle (outer[0].x, outer[0].y, outer[0].z,
229                 outer[2].x, outer[2].y, outer[2].z,
230                 outer[3].x, outer[3].y, outer[3].z,
231                 wireframe_p);
232       triangle (outer[1].x, outer[1].y, outer[1].z,
233                 outer[3].x, outer[3].y, outer[3].z,
234                 outer[2].x, outer[2].y, outer[2].z,
235                 wireframe_p);
236     }
237   else
238     {
239 #     define M01 0
240 #     define M02 1
241 #     define M03 2
242 #     define M12 3
243 #     define M13 4
244 #     define M23 5
245       GL_VECTOR inner[M23+1];
246       GL_VECTOR corner[4];
247
248       inner[M01].x = (outer[0].x + outer[1].x) / 2.0;
249       inner[M01].y = (outer[0].y + outer[1].y) / 2.0;
250       inner[M01].z = (outer[0].z + outer[1].z) / 2.0;
251
252       inner[M02].x = (outer[0].x + outer[2].x) / 2.0;
253       inner[M02].y = (outer[0].y + outer[2].y) / 2.0;
254       inner[M02].z = (outer[0].z + outer[2].z) / 2.0;
255
256       inner[M03].x = (outer[0].x + outer[3].x) / 2.0;
257       inner[M03].y = (outer[0].y + outer[3].y) / 2.0;
258       inner[M03].z = (outer[0].z + outer[3].z) / 2.0;
259
260       inner[M12].x = (outer[1].x + outer[2].x) / 2.0;
261       inner[M12].y = (outer[1].y + outer[2].y) / 2.0;
262       inner[M12].z = (outer[1].z + outer[2].z) / 2.0;
263
264       inner[M13].x = (outer[1].x + outer[3].x) / 2.0;
265       inner[M13].y = (outer[1].y + outer[3].y) / 2.0;
266       inner[M13].z = (outer[1].z + outer[3].z) / 2.0;
267
268       inner[M23].x = (outer[2].x + outer[3].x) / 2.0;
269       inner[M23].y = (outer[2].y + outer[3].y) / 2.0;
270       inner[M23].z = (outer[2].z + outer[3].z) / 2.0;
271
272       countdown--;
273
274       corner[0] = outer[0];
275       corner[1] = inner[M01];
276       corner[2] = inner[M02];
277       corner[3] = inner[M03];
278       four_tetras (corner, wireframe_p, countdown);
279
280       corner[0] = inner[M01];
281       corner[1] = outer[1];
282       corner[2] = inner[M12];
283       corner[3] = inner[M13];
284       four_tetras (corner, wireframe_p, countdown);
285
286       corner[0] = inner[M02];
287       corner[1] = inner[M12];
288       corner[2] = outer[2];
289       corner[3] = inner[M23];
290       four_tetras (corner, wireframe_p, countdown);
291
292       corner[0] = inner[M03];
293       corner[1] = inner[M13];
294       corner[2] = inner[M23];
295       corner[3] = outer[3];
296       four_tetras (corner, wireframe_p, countdown);
297     }
298 }
299
300
301 static void
302 compile_gasket(ModeInfo *mi)
303 {
304   Bool wireframe_p = MI_IS_WIREFRAME(mi);
305   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
306
307   GL_VECTOR   vertex[5];
308
309   /* define verticies */
310   vertex[0].x =  0.5; 
311   vertex[0].y = -(1.0/3.0)*sqrt((2.0/3.0));
312   vertex[0].z = -sqrt(3.0)/6.0;
313
314   vertex[1].x = -0.5; 
315   vertex[1].y = -(1.0/3.0)*sqrt((2.0/3.0)); 
316   vertex[1].z = -sqrt(3.0)/6.0; 
317
318   vertex[2].x = 0.0; 
319   vertex[2].y = (2.0/3.0)*sqrt((2.0/3.0));
320   vertex[2].z = -sqrt(3.0)/6.0; 
321
322   vertex[3].x = 0.0; 
323   vertex[3].y = 0.0; 
324   vertex[3].z = sqrt(3.0)/3.0; 
325
326   vertex[4].x = 0.0;
327   vertex[4].y = 0.0; 
328   vertex[4].z = 0.0;
329   
330   four_tetras (vertex, wireframe_p,
331                (gp->current_depth < 0
332                 ? -gp->current_depth : gp->current_depth));
333 }
334
335 static void
336 draw(ModeInfo *mi)
337 {
338   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
339   static int tick = 0;
340   
341   static float position0[] = {-0.5,  1.2, 0.5, 0.0};
342   static float ambient0[]  = {0.4, 0.6, 0.4, 1.0};
343   static float spec[]      = {0.7, 0.7, 0.7, 1.0};
344   static float shine[]     = {70.0};
345
346   glLightfv(GL_LIGHT0, GL_POSITION,  position0);
347   glLightfv(GL_LIGHT0, GL_AMBIENT,   ambient0);
348   glLightfv(GL_LIGHT0, GL_SPECULAR,  spec);
349   glLightfv(GL_LIGHT0, GL_SHININESS, shine);
350   glLightfv(GL_LIGHT0, GL_DIFFUSE,   gp->light_colour);
351
352   glShadeModel(GL_SMOOTH);
353
354   glEnable(GL_LIGHTING);
355   glEnable(GL_LIGHT0);
356
357   glEnable(GL_DEPTH_TEST);
358   glEnable(GL_NORMALIZE);
359   glEnable(GL_CULL_FACE);
360
361   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
362
363   glPushMatrix();
364   glTranslatef( gp->pos[0], gp->pos[1], gp->pos[2] );  
365
366   glPushMatrix();
367   glRotatef(2*gp->angle, 1.0, 0.0, 0.0);
368   glRotatef(3*gp->angle, 0.0, 1.0, 0.0);
369   glRotatef(  gp->angle, 0.0, 0.0, 1.0);
370   glScalef( 8.0, 8.0, 8.0 );
371   glCallList(gp->gasket1);
372   
373   glPopMatrix();
374
375   glPopMatrix();
376
377
378   if (tick++ >= speed)
379     {
380       tick = 0;
381       if (gp->current_depth >= max_depth)
382         gp->current_depth = -max_depth;
383       gp->current_depth++;
384
385       glDeleteLists (gp->gasket1, 1);
386       glNewList (gp->gasket1, GL_COMPILE);
387       compile_gasket (mi);
388       glEndList();
389
390       /* do the colour change */
391       gp->light_colour[0] = 3.0*SINF(gp->angle/20.0) + 4.0;
392       gp->light_colour[1] = 3.0*SINF(gp->angle/30.0) + 4.0;
393       gp->light_colour[2] = 3.0*SINF(gp->angle/60.0) + 4.0;
394     }
395 }
396
397
398 /* new window size or exposure */
399 static void
400 reshape(int width, int height)
401 {
402   GLfloat h = (GLfloat) height / (GLfloat) width;
403
404   glViewport(0, 0, (GLint) width, (GLint) height);
405   glMatrixMode(GL_PROJECTION);
406   glLoadIdentity();
407
408   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
409   gluLookAt( 0.0, 0.0, 15.0,
410              0.0, 0.0, 0.0,
411              0.0, 1.0, 0.0);
412   glMatrixMode(GL_MODELVIEW);
413   glLoadIdentity();
414   glTranslatef(0.0, 0.0, -15.0);
415   
416   /* The depth buffer will be cleared, if needed, before the
417   * next frame.  Right now we just want to black the screen.
418   */
419   glClear(GL_COLOR_BUFFER_BIT);
420 }
421
422 static void
423 pinit(ModeInfo *mi)
424 {
425   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
426
427   gp->xinc = 0.1*(1.0*random()/RAND_MAX);
428   gp->yinc = 0.1*(1.0*random()/RAND_MAX);
429   gp->zinc = 0.1*(1.0*random()/RAND_MAX);
430   gp->light_colour[0] = 6.0;
431   gp->light_colour[1] = 6.0;
432   gp->light_colour[2] = 6.0;
433   gp->light_colour[3] = 1.0;
434   gp->pos[0] = 0.0;     
435   gp->pos[1] = 0.0;
436   gp->pos[2] = 0.0;    
437   /* draw the gasket */
438   gp->gasket1 = glGenLists(1);
439   gp->current_depth = 1;       /* start out at level 1, not 0 */
440   glNewList(gp->gasket1, GL_COMPILE);
441     compile_gasket(mi);
442   glEndList();
443 }
444
445 void
446 init_gasket(ModeInfo *mi)
447 {
448   int           screen = MI_SCREEN(mi);
449   gasketstruct *gp;
450
451   if (gasket == NULL)
452   {
453     if ((gasket = (gasketstruct *) calloc(MI_NUM_SCREENS(mi),
454                                               sizeof (gasketstruct))) == NULL)
455         return;
456   }
457   gp = &gasket[screen];
458
459   gp->window = MI_WINDOW(mi);
460   gp->view_rotx = NRAND(360);
461   gp->view_roty = NRAND(360);
462   gp->view_rotz = NRAND(360);
463   gp->angle = NRAND(360)/90.0;
464
465   if ((gp->glx_context = init_GL(mi)) != NULL)
466   {
467     reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
468     pinit(mi);
469   }
470   else
471   {
472     MI_CLEARWINDOW(mi);
473   }
474 }
475
476 void
477 draw_gasket(ModeInfo * mi)
478 {
479   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
480   Display      *display = MI_DISPLAY(mi);
481   Window        window = MI_WINDOW(mi);
482   int           angle_incr = 1;
483   int           rot_incr = 1;/*MI_COUNT(mi) ? MI_COUNT(mi) : 1;*/
484
485   if (!gp->glx_context) return;
486
487   glDrawBuffer(GL_BACK);
488
489   if (max_depth > 10)
490     max_depth = 10;
491
492   glXMakeCurrent(display, window, *(gp->glx_context));
493   draw(mi);
494
495   /* rotate */
496   gp->angle = (int) (gp->angle + angle_incr) % 360;
497   if ( FABSF( gp->pos[0] ) > 8.0 ) gp->xinc = -1.0 * gp->xinc;
498   if ( FABSF( gp->pos[1] ) > 6.0 ) gp->yinc = -1.0 * gp->yinc;
499   if ( FABSF( gp->pos[2] ) >15.0 ) gp->zinc = -1.0 * gp->zinc;
500   gp->pos[0] += gp->xinc;
501   gp->pos[1] += gp->yinc;
502   gp->pos[2] += gp->zinc;    
503   gp->view_rotx = (int) (gp->view_rotx + rot_incr) % 360;
504   gp->view_roty = (int) (gp->view_roty +(rot_incr/2.0)) % 360;
505   gp->view_rotz = (int) (gp->view_rotz +(rot_incr/3.0)) % 360;
506
507   glFinish();
508   glXSwapBuffers(display, window);
509 }
510
511 void
512 release_gasket(ModeInfo * mi)
513 {
514   if (gasket != NULL)
515   {
516     int         screen;
517
518     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
519     {
520       gasketstruct *gp = &gasket[screen];
521
522       if (gp->glx_context)
523       {
524         /* Display lists MUST be freed while their glXContext is current. */
525         glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
526
527         if (glIsList(gp->gasket1)) glDeleteLists(gp->gasket1, 1);
528       }
529     }
530     (void) free((void *) gasket);
531     gasket = NULL;
532   }
533   FreeAllGL(mi);
534 }
535
536
537 /*********************************************************/
538
539 #endif