8e946659b9a8f6dc6f51290290b991c88818c1a0
[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 HACK_RESHAPE                           reshape_gasket
42 # define gasket_opts                            xlockmore_opts
43 # define DEFAULTS                                       "*count:                1       \n"                     \
44                                                                         "*cycles:               9999    \n"                     \
45                                                                         "*delay:                20000   \n"                     \
46                                                                         "*maxDepth:             5       \n"                     \
47                                                                         "*speed:                150     \n"                     \
48                                                                         "*showFPS:      False   \n"                     \
49                                                                         "*wireframe:    False   \n"
50 # include "xlockmore.h"         /* from the xscreensaver distribution */
51 #else  /* !STANDALONE */
52 # include "xlock.h"                     /* from the xlockmore distribution */
53 #endif /* !STANDALONE */
54
55 #ifdef USE_GL
56
57 #undef countof
58 #define countof(x) (sizeof((x))/sizeof((*x)))
59
60 static int max_depth;
61 static int speed;
62 static XrmOptionDescRec opts[] = {
63   {"-depth", ".sierpinski3d.maxDepth", XrmoptionSepArg, (caddr_t) 0 },
64   {"-speed", ".sierpinski3d.speed",    XrmoptionSepArg, (caddr_t) 0 }
65 };
66
67 static argtype vars[] = {
68   {(caddr_t *) &max_depth, "maxDepth", "MaxDepth", "5", t_Int},
69   {(caddr_t *) &speed,     "speed",    "Speed",   "150", t_Int},
70 };
71
72
73 ModeSpecOpt gasket_opts = {countof(opts), opts, countof(vars), vars, NULL};
74
75 #ifdef USE_MODULES
76 ModStruct   gasket_description =
77 {"gasket", "init_gasket", "draw_gasket", "release_gasket",
78  "draw_gasket", "init_gasket", NULL, &gasket_opts,
79  1000, 1, 2, 1, 4, 1.0, "",
80  "Shows GL's Sierpinski gasket", 0, NULL};
81
82 #endif
83
84 typedef struct{
85   GLfloat x;
86   GLfloat y;
87   GLfloat z;
88 } GL_VECTOR;
89
90 typedef struct {
91   GLfloat rotx, roty, rotz;        /* current object rotation */
92   GLfloat dx, dy, dz;              /* current rotational velocity */
93   GLfloat ddx, ddy, ddz;           /* current rotational acceleration */
94   GLfloat d_max;                           /* max velocity */
95
96   GLfloat     angle;
97   GLuint      gasket1;
98   GLXContext *glx_context;
99   Window      window;
100
101   int current_depth;
102
103   int ncolors;
104   XColor *colors;
105   int ccolor;
106
107 } gasketstruct;
108
109 static gasketstruct *gasket = NULL;
110
111 #include <GL/glu.h>
112
113 /* static GLuint limit; */
114
115 \f
116 /* Computing normal vectors (thanks to Nat Friedman <ndf@mit.edu>)
117  */
118
119 typedef struct vector {
120   GLfloat x, y, z;
121 } vector;
122
123 typedef struct plane {
124   vector p1, p2, p3;
125 } plane;
126
127 static void
128 vector_set(vector *v, GLfloat x, GLfloat y, GLfloat z)
129 {
130   v->x = x;
131   v->y = y;
132   v->z = z;
133 }
134
135 static void
136 vector_cross(vector v1, vector v2, vector *v3)
137 {
138   v3->x = (v1.y * v2.z) - (v1.z * v2.y);
139   v3->y = (v1.z * v2.x) - (v1.x * v2.z);
140   v3->z = (v1.x * v2.y) - (v1.y * v2.x);
141 }
142
143 static void
144 vector_subtract(vector v1, vector v2, vector *res)
145 {
146   res->x = v1.x - v2.x;
147   res->y = v1.y - v2.y;
148   res->z = v1.z - v2.z;
149 }
150
151 static void
152 plane_normal(plane p, vector *n)
153 {
154   vector v1, v2;
155   vector_subtract(p.p1, p.p2, &v1);
156   vector_subtract(p.p1, p.p3, &v2);
157   vector_cross(v2, v1, n);
158 }
159
160 static void
161 do_normal(GLfloat x1, GLfloat y1, GLfloat z1,
162           GLfloat x2, GLfloat y2, GLfloat z2,
163           GLfloat x3, GLfloat y3, GLfloat z3)
164 {
165   plane plane;
166   vector n;
167   vector_set(&plane.p1, x1, y1, z1);
168   vector_set(&plane.p2, x2, y2, z2);
169   vector_set(&plane.p3, x3, y3, z3);
170   plane_normal(plane, &n);
171   n.x = -n.x; n.y = -n.y; n.z = -n.z;
172
173   glNormal3f(n.x, n.y, n.z);
174
175 #ifdef DEBUG
176   /* Draw a line in the direction of this face's normal. */
177   {
178     GLfloat ax = n.x > 0 ? n.x : -n.x;
179     GLfloat ay = n.y > 0 ? n.y : -n.y;
180     GLfloat az = n.z > 0 ? n.z : -n.z;
181     GLfloat mx = (x1 + x2 + x3) / 3;
182     GLfloat my = (y1 + y2 + y3) / 3;
183     GLfloat mz = (z1 + z2 + z3) / 3;
184     GLfloat xx, yy, zz;
185
186     GLfloat max = ax > ay ? ax : ay;
187     if (az > max) max = az;
188     max *= 2;
189     xx = n.x / max;
190     yy = n.y / max;
191     zz = n.z / max;
192
193     glBegin(GL_LINE_LOOP);
194     glVertex3f(mx, my, mz);
195     glVertex3f(mx+xx, my+yy, mz+zz);
196     glEnd();
197   }
198 #endif /* DEBUG */
199 }
200
201 \f
202
203 static void
204 triangle (GLfloat x1, GLfloat y1, GLfloat z1,
205           GLfloat x2, GLfloat y2, GLfloat z2,
206           GLfloat x3, GLfloat y3, GLfloat z3,
207           Bool wireframe_p)
208 {
209   if (wireframe_p)
210     glBegin (GL_LINE_LOOP);
211   else
212     {
213       do_normal (x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
214       glBegin (GL_TRIANGLES);
215     }
216   glVertex3f (x1, y1, z1);
217   glVertex3f (x2, y2, z2);
218   glVertex3f (x3, y3, z3);
219   glEnd();
220 }
221
222 static void
223 four_tetras (GL_VECTOR *outer, Bool wireframe_p, int countdown)
224 {
225   if (countdown <= 0)
226     {
227       triangle (outer[0].x, outer[0].y, outer[0].z,
228                 outer[1].x, outer[1].y, outer[1].z,
229                 outer[2].x, outer[2].y, outer[2].z,
230                 wireframe_p);
231       triangle (outer[0].x, outer[0].y, outer[0].z,
232                 outer[3].x, outer[3].y, outer[3].z,
233                 outer[1].x, outer[1].y, outer[1].z,
234                 wireframe_p);
235       triangle (outer[0].x, outer[0].y, outer[0].z,
236                 outer[2].x, outer[2].y, outer[2].z,
237                 outer[3].x, outer[3].y, outer[3].z,
238                 wireframe_p);
239       triangle (outer[1].x, outer[1].y, outer[1].z,
240                 outer[3].x, outer[3].y, outer[3].z,
241                 outer[2].x, outer[2].y, outer[2].z,
242                 wireframe_p);
243     }
244   else
245     {
246 #     define M01 0
247 #     define M02 1
248 #     define M03 2
249 #     define M12 3
250 #     define M13 4
251 #     define M23 5
252       GL_VECTOR inner[M23+1];
253       GL_VECTOR corner[4];
254
255       inner[M01].x = (outer[0].x + outer[1].x) / 2.0;
256       inner[M01].y = (outer[0].y + outer[1].y) / 2.0;
257       inner[M01].z = (outer[0].z + outer[1].z) / 2.0;
258
259       inner[M02].x = (outer[0].x + outer[2].x) / 2.0;
260       inner[M02].y = (outer[0].y + outer[2].y) / 2.0;
261       inner[M02].z = (outer[0].z + outer[2].z) / 2.0;
262
263       inner[M03].x = (outer[0].x + outer[3].x) / 2.0;
264       inner[M03].y = (outer[0].y + outer[3].y) / 2.0;
265       inner[M03].z = (outer[0].z + outer[3].z) / 2.0;
266
267       inner[M12].x = (outer[1].x + outer[2].x) / 2.0;
268       inner[M12].y = (outer[1].y + outer[2].y) / 2.0;
269       inner[M12].z = (outer[1].z + outer[2].z) / 2.0;
270
271       inner[M13].x = (outer[1].x + outer[3].x) / 2.0;
272       inner[M13].y = (outer[1].y + outer[3].y) / 2.0;
273       inner[M13].z = (outer[1].z + outer[3].z) / 2.0;
274
275       inner[M23].x = (outer[2].x + outer[3].x) / 2.0;
276       inner[M23].y = (outer[2].y + outer[3].y) / 2.0;
277       inner[M23].z = (outer[2].z + outer[3].z) / 2.0;
278
279       countdown--;
280
281       corner[0] = outer[0];
282       corner[1] = inner[M01];
283       corner[2] = inner[M02];
284       corner[3] = inner[M03];
285       four_tetras (corner, wireframe_p, countdown);
286
287       corner[0] = inner[M01];
288       corner[1] = outer[1];
289       corner[2] = inner[M12];
290       corner[3] = inner[M13];
291       four_tetras (corner, wireframe_p, countdown);
292
293       corner[0] = inner[M02];
294       corner[1] = inner[M12];
295       corner[2] = outer[2];
296       corner[3] = inner[M23];
297       four_tetras (corner, wireframe_p, countdown);
298
299       corner[0] = inner[M03];
300       corner[1] = inner[M13];
301       corner[2] = inner[M23];
302       corner[3] = outer[3];
303       four_tetras (corner, wireframe_p, countdown);
304     }
305 }
306
307
308 static void
309 compile_gasket(ModeInfo *mi)
310 {
311   Bool wireframe_p = MI_IS_WIREFRAME(mi);
312   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
313
314   GL_VECTOR   vertex[5];
315
316   /* define verticies */
317   vertex[0].x =  0.5; 
318   vertex[0].y = -(1.0/3.0)*sqrt((2.0/3.0));
319   vertex[0].z = -sqrt(3.0)/6.0;
320
321   vertex[1].x = -0.5; 
322   vertex[1].y = -(1.0/3.0)*sqrt((2.0/3.0)); 
323   vertex[1].z = -sqrt(3.0)/6.0; 
324
325   vertex[2].x = 0.0; 
326   vertex[2].y = (2.0/3.0)*sqrt((2.0/3.0));
327   vertex[2].z = -sqrt(3.0)/6.0; 
328
329   vertex[3].x = 0.0; 
330   vertex[3].y = 0.0; 
331   vertex[3].z = sqrt(3.0)/3.0; 
332
333   vertex[4].x = 0.0;
334   vertex[4].y = 0.0; 
335   vertex[4].z = 0.0;
336   
337   four_tetras (vertex, wireframe_p,
338                (gp->current_depth < 0
339                 ? -gp->current_depth : gp->current_depth));
340 }
341
342 static void
343 draw(ModeInfo *mi)
344 {
345   Bool wireframe_p = MI_IS_WIREFRAME(mi);
346   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
347   static int tick = 0;
348   
349   static GLfloat pos[4] = {-4.0, 3.0, 10.0, 1.0};
350   static float white[]  = {1.0, 1.0, 1.0, 1.0};
351   static float color[]  = {0.0, 0.0, 0.0, 1.0};
352
353   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
354
355   if (!wireframe_p)
356     {
357       glColor4fv (white);
358
359       glLightfv(GL_LIGHT0, GL_POSITION,  pos);
360
361       color[0] = gp->colors[gp->ccolor].red   / 65536.0;
362       color[1] = gp->colors[gp->ccolor].green / 65536.0;
363       color[2] = gp->colors[gp->ccolor].blue  / 65536.0;
364       gp->ccolor++;
365       if (gp->ccolor >= gp->ncolors) gp->ccolor = 0;
366
367       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
368
369       glShadeModel(GL_SMOOTH);
370
371       glEnable(GL_LIGHTING);
372       glEnable(GL_LIGHT0);
373     }
374
375   glEnable(GL_DEPTH_TEST);
376   glEnable(GL_NORMALIZE);
377   glEnable(GL_CULL_FACE);
378
379   glPushMatrix();
380
381   {
382     static int frame = 0;
383     GLfloat x, y, z;
384
385 #   define SINOID(SCALE,SIZE) \
386       ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
387     x = SINOID(0.0071, 8.0);
388     y = SINOID(0.0053, 6.0);
389     z = SINOID(0.0037, 15.0);
390     frame++;
391     glTranslatef(x, y, z);
392
393     x = gp->rotx;
394     y = gp->roty;
395     z = gp->rotz;
396     if (x < 0) x = 1 - (x + 1);
397     if (y < 0) y = 1 - (y + 1);
398     if (z < 0) z = 1 - (z + 1);
399     glRotatef(x * 360, 1.0, 0.0, 0.0);
400     glRotatef(y * 360, 0.0, 1.0, 0.0);
401     glRotatef(z * 360, 0.0, 0.0, 1.0);
402   }
403
404   glScalef( 8.0, 8.0, 8.0 );
405   glCallList(gp->gasket1);
406
407   glPopMatrix();
408
409
410   if (tick++ >= speed)
411     {
412       tick = 0;
413       if (gp->current_depth >= max_depth)
414         gp->current_depth = -max_depth;
415       gp->current_depth++;
416
417       glDeleteLists (gp->gasket1, 1);
418       glNewList (gp->gasket1, GL_COMPILE);
419       compile_gasket (mi);
420       glEndList();
421
422     }
423 }
424
425
426 /* new window size or exposure */
427 void
428 reshape_gasket(ModeInfo *mi, int width, int height)
429 {
430   GLfloat h = (GLfloat) height / (GLfloat) width;
431
432   glViewport(0, 0, (GLint) width, (GLint) height);
433   glMatrixMode(GL_PROJECTION);
434   glLoadIdentity();
435
436   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
437   gluLookAt( 0.0, 0.0, 15.0,
438              0.0, 0.0, 0.0,
439              0.0, 1.0, 0.0);
440   glMatrixMode(GL_MODELVIEW);
441   glLoadIdentity();
442   glTranslatef(0.0, 0.0, -15.0);
443   
444   glClear(GL_COLOR_BUFFER_BIT);
445 }
446
447 static void
448 pinit(ModeInfo *mi)
449 {
450   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
451
452   /* draw the gasket */
453   gp->gasket1 = glGenLists(1);
454   gp->current_depth = 1;       /* start out at level 1, not 0 */
455   glNewList(gp->gasket1, GL_COMPILE);
456     compile_gasket(mi);
457   glEndList();
458 }
459
460
461
462 /* lifted from lament.c */
463 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
464 #define RANDSIGN() ((random() & 1) ? 1 : -1)
465
466 static void
467 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
468 {
469   double ppos = *pos;
470
471   /* tick position */
472   if (ppos < 0)
473     ppos = -(ppos + *v);
474   else
475     ppos += *v;
476
477   if (ppos > 1.0)
478     ppos -= 1.0;
479   else if (ppos < 0)
480     ppos += 1.0;
481
482   if (ppos < 0) abort();
483   if (ppos > 1.0) abort();
484   *pos = (*pos > 0 ? ppos : -ppos);
485
486   /* accelerate */
487   *v += *dv;
488
489   /* clamp velocity */
490   if (*v > max_v || *v < -max_v)
491     {
492       *dv = -*dv;
493     }
494   /* If it stops, start it going in the other direction. */
495   else if (*v < 0)
496     {
497       if (random() % 4)
498         {
499           *v = 0;
500
501           /* keep going in the same direction */
502           if (random() % 2)
503             *dv = 0;
504           else if (*dv < 0)
505             *dv = -*dv;
506         }
507       else
508         {
509           /* reverse gears */
510           *v = -*v;
511           *dv = -*dv;
512           *pos = -*pos;
513         }
514     }
515
516   /* Alter direction of rotational acceleration randomly. */
517   if (! (random() % 120))
518     *dv = -*dv;
519
520   /* Change acceleration very occasionally. */
521   if (! (random() % 200))
522     {
523       if (*dv == 0)
524         *dv = 0.00001;
525       else if (random() & 1)
526         *dv *= 1.2;
527       else
528         *dv *= 0.8;
529     }
530 }
531
532
533 void
534 init_gasket(ModeInfo *mi)
535 {
536   Bool wireframe_p = MI_IS_WIREFRAME(mi);
537   int           screen = MI_SCREEN(mi);
538   gasketstruct *gp;
539
540   if (gasket == NULL)
541   {
542     if ((gasket = (gasketstruct *) calloc(MI_NUM_SCREENS(mi),
543                                               sizeof (gasketstruct))) == NULL)
544         return;
545   }
546   gp = &gasket[screen];
547
548   gp->window = MI_WINDOW(mi);
549
550   gp->rotx = frand(1.0) * RANDSIGN();
551   gp->roty = frand(1.0) * RANDSIGN();
552   gp->rotz = frand(1.0) * RANDSIGN();
553
554   /* bell curve from 0-1.5 degrees, avg 0.75 */
555   gp->dx = (frand(1) + frand(1) + frand(1)) / (360*2);
556   gp->dy = (frand(1) + frand(1) + frand(1)) / (360*2);
557   gp->dz = (frand(1) + frand(1) + frand(1)) / (360*2);
558
559   gp->d_max = gp->dx * 2;
560
561   gp->ddx = 0.00006 + frand(0.00003);
562   gp->ddy = 0.00006 + frand(0.00003);
563   gp->ddz = 0.00006 + frand(0.00003);
564
565   gp->ddx = 0.00001;
566   gp->ddy = 0.00001;
567   gp->ddz = 0.00001;
568
569
570   gp->ncolors = 255;
571   gp->colors = (XColor *) calloc(gp->ncolors, sizeof(XColor));
572   make_smooth_colormap (0, 0, 0,
573                         gp->colors, &gp->ncolors,
574                         False, 0, False);
575
576   if ((gp->glx_context = init_GL(mi)) != NULL)
577   {
578     reshape_gasket(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
579     pinit(mi);
580   }
581   else
582   {
583     MI_CLEARWINDOW(mi);
584   }
585 }
586
587 void
588 draw_gasket(ModeInfo * mi)
589 {
590   gasketstruct *gp = &gasket[MI_SCREEN(mi)];
591   Display      *display = MI_DISPLAY(mi);
592   Window        window = MI_WINDOW(mi);
593   int           angle_incr = 1;
594
595   if (!gp->glx_context) return;
596
597   glDrawBuffer(GL_BACK);
598
599   if (max_depth > 10)
600     max_depth = 10;
601
602   glXMakeCurrent(display, window, *(gp->glx_context));
603   draw(mi);
604
605   /* rotate */
606   gp->angle = (int) (gp->angle + angle_incr) % 360;
607
608   rotate(&gp->rotx, &gp->dx, &gp->ddx, gp->d_max);
609   rotate(&gp->roty, &gp->dy, &gp->ddy, gp->d_max);
610   rotate(&gp->rotz, &gp->dz, &gp->ddz, gp->d_max);
611
612   if (mi->fps_p) do_fps (mi);
613   glFinish();
614   glXSwapBuffers(display, window);
615 }
616
617 void
618 release_gasket(ModeInfo * mi)
619 {
620   if (gasket != NULL)
621   {
622     int         screen;
623
624     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
625     {
626       gasketstruct *gp = &gasket[screen];
627
628       if (gp->glx_context)
629       {
630         /* Display lists MUST be freed while their glXContext is current. */
631         glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
632
633         if (glIsList(gp->gasket1)) glDeleteLists(gp->gasket1, 1);
634       }
635     }
636     (void) free((void *) gasket);
637     gasket = NULL;
638   }
639   FreeAllGL(mi);
640 }
641
642
643 /*********************************************************/
644
645 #endif