1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* Sierpinski3D --- 3D sierpinski gasket */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)sierpinski3D.c 00.01 99/11/04 xlockmore";
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.
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.
23 * 1999: written by Tim Robinson <the_luggage@bigfoot.com>
24 * a 3-D representation of the Sierpinski gasket fractal.
26 * 10-Dec-99 jwz rewrote to draw a set of tetrahedrons instead of a
27 * random scattering of points.
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
35 #include <X11/Intrinsic.h>
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" \
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 */
58 #define countof(x) (sizeof((x))/sizeof((*x)))
62 static XrmOptionDescRec opts[] = {
63 {"-depth", ".sierpinski3d.maxDepth", XrmoptionSepArg, (caddr_t) 0 },
64 {"-speed", ".sierpinski3d.speed", XrmoptionSepArg, (caddr_t) 0 }
67 static argtype vars[] = {
68 {(caddr_t *) &max_depth, "maxDepth", "MaxDepth", "5", t_Int},
69 {(caddr_t *) &speed, "speed", "Speed", "150", t_Int},
73 ModeSpecOpt gasket_opts = {countof(opts), opts, countof(vars), vars, NULL};
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};
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 */
98 GLXContext *glx_context;
109 static gasketstruct *gasket = NULL;
113 /* static GLuint limit; */
116 /* Computing normal vectors (thanks to Nat Friedman <ndf@mit.edu>)
119 typedef struct vector {
123 typedef struct plane {
128 vector_set(vector *v, GLfloat x, GLfloat y, GLfloat z)
136 vector_cross(vector v1, vector v2, vector *v3)
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);
144 vector_subtract(vector v1, vector v2, vector *res)
146 res->x = v1.x - v2.x;
147 res->y = v1.y - v2.y;
148 res->z = v1.z - v2.z;
152 plane_normal(plane p, vector *n)
155 vector_subtract(p.p1, p.p2, &v1);
156 vector_subtract(p.p1, p.p3, &v2);
157 vector_cross(v2, v1, n);
161 do_normal(GLfloat x1, GLfloat y1, GLfloat z1,
162 GLfloat x2, GLfloat y2, GLfloat z2,
163 GLfloat x3, GLfloat y3, GLfloat z3)
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;
173 glNormal3f(n.x, n.y, n.z);
176 /* Draw a line in the direction of this face's normal. */
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;
186 GLfloat max = ax > ay ? ax : ay;
187 if (az > max) max = az;
193 glBegin(GL_LINE_LOOP);
194 glVertex3f(mx, my, mz);
195 glVertex3f(mx+xx, my+yy, mz+zz);
204 triangle (GLfloat x1, GLfloat y1, GLfloat z1,
205 GLfloat x2, GLfloat y2, GLfloat z2,
206 GLfloat x3, GLfloat y3, GLfloat z3,
210 glBegin (GL_LINE_LOOP);
213 do_normal (x1, y1, z1, x2, y2, z2, x3, y3, z3);
214 glBegin (GL_TRIANGLES);
216 glVertex3f (x1, y1, z1);
217 glVertex3f (x2, y2, z2);
218 glVertex3f (x3, y3, z3);
223 four_tetras (GL_VECTOR *outer, Bool wireframe_p, int countdown)
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,
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,
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,
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,
252 GL_VECTOR inner[M23+1];
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;
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;
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;
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;
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;
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;
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);
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);
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);
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);
309 compile_gasket(ModeInfo *mi)
311 Bool wireframe_p = MI_IS_WIREFRAME(mi);
312 gasketstruct *gp = &gasket[MI_SCREEN(mi)];
316 /* define verticies */
318 vertex[0].y = -(1.0/3.0)*sqrt((2.0/3.0));
319 vertex[0].z = -sqrt(3.0)/6.0;
322 vertex[1].y = -(1.0/3.0)*sqrt((2.0/3.0));
323 vertex[1].z = -sqrt(3.0)/6.0;
326 vertex[2].y = (2.0/3.0)*sqrt((2.0/3.0));
327 vertex[2].z = -sqrt(3.0)/6.0;
331 vertex[3].z = sqrt(3.0)/3.0;
337 four_tetras (vertex, wireframe_p,
338 (gp->current_depth < 0
339 ? -gp->current_depth : gp->current_depth));
345 Bool wireframe_p = MI_IS_WIREFRAME(mi);
346 gasketstruct *gp = &gasket[MI_SCREEN(mi)];
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};
353 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
359 glLightfv(GL_LIGHT0, GL_POSITION, pos);
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;
365 if (gp->ccolor >= gp->ncolors) gp->ccolor = 0;
367 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
369 glShadeModel(GL_SMOOTH);
371 glEnable(GL_LIGHTING);
375 glEnable(GL_DEPTH_TEST);
376 glEnable(GL_NORMALIZE);
377 glEnable(GL_CULL_FACE);
382 static int frame = 0;
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);
391 glTranslatef(x, y, z);
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);
404 glScalef( 8.0, 8.0, 8.0 );
405 glCallList(gp->gasket1);
413 if (gp->current_depth >= max_depth)
414 gp->current_depth = -max_depth;
417 glDeleteLists (gp->gasket1, 1);
418 glNewList (gp->gasket1, GL_COMPILE);
426 /* new window size or exposure */
428 reshape_gasket(ModeInfo *mi, int width, int height)
430 GLfloat h = (GLfloat) height / (GLfloat) width;
432 glViewport(0, 0, (GLint) width, (GLint) height);
433 glMatrixMode(GL_PROJECTION);
436 gluPerspective( 30.0, 1/h, 1.0, 100.0 );
437 gluLookAt( 0.0, 0.0, 15.0,
440 glMatrixMode(GL_MODELVIEW);
442 glTranslatef(0.0, 0.0, -15.0);
444 glClear(GL_COLOR_BUFFER_BIT);
450 gasketstruct *gp = &gasket[MI_SCREEN(mi)];
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);
462 /* lifted from lament.c */
463 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
464 #define RANDSIGN() ((random() & 1) ? 1 : -1)
467 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
482 if (ppos < 0) abort();
483 if (ppos > 1.0) abort();
484 *pos = (*pos > 0 ? ppos : -ppos);
490 if (*v > max_v || *v < -max_v)
494 /* If it stops, start it going in the other direction. */
501 /* keep going in the same direction */
516 /* Alter direction of rotational acceleration randomly. */
517 if (! (random() % 120))
520 /* Change acceleration very occasionally. */
521 if (! (random() % 200))
525 else if (random() & 1)
534 init_gasket(ModeInfo *mi)
536 int screen = MI_SCREEN(mi);
541 if ((gasket = (gasketstruct *) calloc(MI_NUM_SCREENS(mi),
542 sizeof (gasketstruct))) == NULL)
545 gp = &gasket[screen];
547 gp->window = MI_WINDOW(mi);
549 gp->rotx = frand(1.0) * RANDSIGN();
550 gp->roty = frand(1.0) * RANDSIGN();
551 gp->rotz = frand(1.0) * RANDSIGN();
553 /* bell curve from 0-1.5 degrees, avg 0.75 */
554 gp->dx = (frand(1) + frand(1) + frand(1)) / (360*2);
555 gp->dy = (frand(1) + frand(1) + frand(1)) / (360*2);
556 gp->dz = (frand(1) + frand(1) + frand(1)) / (360*2);
558 gp->d_max = gp->dx * 2;
560 gp->ddx = 0.00006 + frand(0.00003);
561 gp->ddy = 0.00006 + frand(0.00003);
562 gp->ddz = 0.00006 + frand(0.00003);
565 gp->colors = (XColor *) calloc(gp->ncolors, sizeof(XColor));
566 make_smooth_colormap (0, 0, 0,
567 gp->colors, &gp->ncolors,
570 if ((gp->glx_context = init_GL(mi)) != NULL)
572 reshape_gasket(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
582 draw_gasket(ModeInfo * mi)
584 gasketstruct *gp = &gasket[MI_SCREEN(mi)];
585 Display *display = MI_DISPLAY(mi);
586 Window window = MI_WINDOW(mi);
589 if (!gp->glx_context) return;
591 glDrawBuffer(GL_BACK);
596 glXMakeCurrent(display, window, *(gp->glx_context));
600 gp->angle = (int) (gp->angle + angle_incr) % 360;
602 rotate(&gp->rotx, &gp->dx, &gp->ddx, gp->d_max);
603 rotate(&gp->roty, &gp->dy, &gp->ddy, gp->d_max);
604 rotate(&gp->rotz, &gp->dz, &gp->ddz, gp->d_max);
606 if (mi->fps_p) do_fps (mi);
608 glXSwapBuffers(display, window);
612 release_gasket(ModeInfo * mi)
618 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
620 gasketstruct *gp = &gasket[screen];
624 /* Display lists MUST be freed while their glXContext is current. */
625 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
627 if (glIsList(gp->gasket1)) glDeleteLists(gp->gasket1, 1);
630 (void) free((void *) gasket);
637 /*********************************************************/