1 /* -*- Mode: C; tab-width: 4 -*-
2 * superquadrics.c --- 3D mathematical shapes
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)superquadrics.c 4.04 97/07/28 xlockmore";
7 /* Permission to use, copy, modify, and distribute this software and its
8 * documentation for any purpose and without fee is hereby granted,
9 * provided that the above copyright notice appear in all copies and that
10 * both that copyright notice and this permission notice appear in
11 * supporting documentation.
13 * This file is provided AS IS with no warranties of any kind. The author
14 * shall have no liability with respect to the infringement of copyrights,
15 * trade secrets or any patents by this file or any part thereof. In no
16 * event will the author be liable for any lost revenue or profits or
17 * other special, indirect and consequential damages.
19 * Superquadrics were invented by Dr. Alan Barr of Caltech University.
20 * They were first published in "Computer Graphics and Applications",
21 * volume 1, number 1, 1981, in the article "Superquadrics and Angle-
22 * Preserving Transformations." Dr. Barr based the Superquadrics on
23 * Piet Hein's "super ellipses." Super ellipses are like 2D ellipses,
24 * except that the formula includes an exponent, raising its X and Y
25 * values to a (fractional) power, and allowing them to gradually
26 * change from round to square edges. Superquadrics extend this
27 * idea into 3 dimensions, using two exponents to modify a
28 * quadric surface in a similar fashion.
31 * 30-Mar-97: Turned into a module for xlockmore 4.02 alpha. The code
32 * is almost unrecognizable now from the first revision, except for
33 * a few remaining two-letter variable names. I still don't have
34 * the normal vectors working right (I wrote the buggy normal vector
35 * code myself, can you tell?)
36 * 07-Jan-97: A legend reborn; Superquadrics make an appearance as a
37 * real OpenGL program written in C. I can even render them with
38 * proper lighting and specular highlights. Gee, they look almost
39 * as good now as the original color plates of them that my uncle
40 * showed me as a child in 1981. I don't know what computer hardware
41 * he was using at the time, but it's taken a couple decades for the
42 * PC clone hardware to catch up to it.
43 * 05-Jan-97: After almost a decade, Superquadrics had almost faded away
44 * into the myths and folklore of all the things my brother and I played
45 * with on computers when we were kids with too much time on our hands.
46 * I had since gotten involved in Unix, OpenGL, and other things.
47 * A sudden flash of inspiration caused me to dig out the old Pascal
48 * source code, run it through p2c, and start ripping away the old
49 * wireframe rendering code, to be replaced by OpenGL.
50 * Late 1989 or early 1990: Around this time I did the Turbo Pascal
51 * port of the Superquadrics. Unfortunately, many of the original variable
52 * names remained the same from the C= 64 original. This was unfortunate
53 * because BASIC on the c64 only allowed 2-letter, global variable names.
54 * But the speed improvement over BASIC was very impressive at the time.
55 * Thanksgiving, 1987: Written. My uncle Al, who invented Superquadrics some
56 * years earlier, came to visit us. I was a high school kid at the time,
57 * with nothing more than a Commodore 64. Somehow we wrote this program,
58 * (he did the math obviously, I just coded it into BASIC for the c64).
59 * Yeah, 320x200 resolution, colorless white wireframe, and half an hour
60 * rendering time per superquadric. PLOT x,y. THOSE were the days.
61 * In the following years I would port Superquadrics to AppleBASIC,
62 * AmigaBASIC, and then Turbo Pascal for IBM clones. 5 minutes on a 286!
63 * Talk about fast rendering! But these days, when my Pentium 166 runs
64 * the same program, the superquadric will already be waiting on the
65 * screen before my monitor can change frequency from text to graphics
66 * mode. Can't time the number of minutes that way! Darn ;)
72 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
73 * otherwise caddr_t is not defined correctly
76 #include <X11/Intrinsic.h>
79 # define PROGCLASS "Superquadrics"
80 # define HACK_INIT init_superquadrics
81 # define HACK_DRAW draw_superquadrics
82 # define superquadrics_opts xlockmore_opts
83 # define DEFAULTS "*count: 25 \n" \
86 "*wireframe: False \n"
87 # include "xlockmore.h" /* from the xscreensaver distribution */
88 #else /* !STANDALONE */
89 # include "xlock.h" /* from the xlockmore distribution */
90 #endif /* !STANDALONE */
95 * Note for low-CPU-speed machines: If your frame rate is so low that
96 * attempts at animation appear futile, try using "-cycles 1", which puts
97 * Superquadrics into kind of a slide-show mode. It will still use up
98 * all of your CPU power, but it may look nicer.
101 #define DEF_SPINSPEED "5.0"
103 static float spinspeed;
105 static XrmOptionDescRec opts[] =
107 {"-spinspeed", ".superquadrics.spinspeed", XrmoptionSepArg, (caddr_t) NULL}
109 static argtype vars[] =
111 {(caddr_t *) & spinspeed, "spinspeed", "Spinspeed", DEF_SPINSPEED, t_Float}
113 static OptionStruct desc[] =
115 {"-spinspeed num", "speed of rotation, in degrees per frame"}
118 ModeSpecOpt superquadrics_opts =
119 {1, opts, 1, vars, desc};
126 typedef double dimi[MaxRes + 1];
129 double xExponent, yExponent;
130 GLfloat r[4], g[4], b[4];
136 GLXContext glx_context;
137 int dist, wireframe, flatshade, shownorms, maxcount, maxwait;
138 int counter, viewcount, viewwait, mono;
139 GLfloat curmat[4][4], rotx, roty, rotz, spinspeed;
140 /* In dimi: the first letter stands for cosine/sine, the second
141 * stands for North, South, East, or West. I think.
143 dimi cs, se, sw, sn, ss, ce, cw, cn, Prevxx, Prevyy, Prevzz,
144 Prevxn, Prevyn, Prevzn;
145 double xExponent, yExponent, Mode;
148 } superquadricsstruct;
150 static superquadricsstruct *superquadrics = NULL;
152 #define CLIP_NORMALS 10000.0
154 static void ReshapeSuperquadrics(int w, int h);
159 return ((int) (((float) range) * LRAND() / (MAXRAND)));
165 return (LRAND() / (MAXRAND));
168 /* Some old, old, OLD code follows. Ahh this takes me back..... */
170 /* Output from p2c, the Pascal-to-C translator */
171 /* From input file "squad.pas" */
174 XtoY(double x, double y)
178 /* This is NOT your typical raise-X-to-the-Y-power function. Do not attempt
179 * to replace this with a standard exponent function. If you must, just
180 * replace the "a = exp(y * log(z));" line with something faster.
189 if (a > CLIP_NORMALS)
198 Sine(double x, double e)
200 /* This is just the sine wave raised to the exponent. BUT, you can't
201 * raise negative numbers to fractional exponents. So we have a special
202 * XtoY routune which handles it in a way useful to superquadrics.
205 return (XtoY(sin(x), e));
210 Cosine(double x, double e)
212 return (XtoY(cos(x), e));
217 MakeUpStuff(int allstuff, superquadricsstruct * sp)
219 static int pats[4][4] =
229 GLfloat r, g, b, r2, g2, b2;
233 if (sp->maxcount < 2)
235 dostuff = allstuff * 15;
237 dostuff = myrand(3) + 1;
238 if (myrand(2) || (dostuff & 1))
244 sp->later.xExponent = (((long) floor(myrandreal() * 250 + 0.5)) / 100.0) + 0.1;
245 sp->later.yExponent = (((long) floor(myrandreal() * 250 + 0.5)) / 100.0) + 0.1;
247 /* Increase the 2.0 .. 2.5 range to 2.0 .. 3.0 */
248 if (sp->later.xExponent > 2.0)
249 sp->later.xExponent = (sp->later.xExponent * 2.0) - 2.0;
250 if (sp->later.yExponent > 2.0)
251 sp->later.yExponent = (sp->later.yExponent * 2.0) - 2.0;
255 sp->later.Mode = myrand(3L) + 1;
256 } while (!allstuff && (sp->later.Mode == sp->now.Mode));
257 /* On init: make sure it can stay in mode 1 if it feels like it. */
261 b = g = r = (GLfloat) (140 + myrand(100)) / 255.0;
262 b2 = g2 = r2 = ((r > 0.69) ? (1.0 - r) : r);
264 r = (GLfloat) (40 + myrand(200)) / 255.0;
265 g = (GLfloat) (40 + myrand(200)) / 255.0;
266 b = (GLfloat) (40 + myrand(200)) / 255.0;
268 r2 = ((myrand(4) && ((r < 0.31) || (r > 0.69))) ? (1.0 - r) : r);
269 g2 = ((myrand(4) && ((g < 0.31) || (g > 0.69))) ? (1.0 - g) : g);
270 b2 = ((myrand(4) && ((b < 0.31) || (b > 0.69))) ? (1.0 - b) : b);
274 for (t = 0; t < 4; ++t) {
275 sp->later.r[t] = pats[pat][t] ? r : r2;
276 sp->later.g[t] = pats[pat][t] ? g : g2;
277 sp->later.b[t] = pats[pat][t] ? b : b2;
281 sp->later.rotx = myrand(360) - 180;
282 sp->later.rotz = myrand(160) - 80;
287 inputs(superquadricsstruct * sp)
290 double u, v, mode3, cn3, inverter2, flatu, flatv;
292 if (sp->Mode < 1.000001) {
296 } else if (sp->Mode < 2.000001) {
298 cn3 = (sp->Mode - 1.0) * 1.5;
299 inverter2 = (sp->Mode - 1.0) * -2.0 + 1.0;
301 mode3 = (sp->Mode - 1.0);
302 cn3 = (sp->Mode - 2.0) / 2.0 + 1.5;
307 flatu = M_PI / (sp->resolution - 1);
308 flatv = mode3 * M_PI / ((sp->resolution - 1) * 2);
313 /* (void) printf("Calculating....\n"); */
314 for (iv = 1; iv <= sp->resolution; iv++) {
316 /* u ranges from PI down to -PI */
317 u = (1 - iv) * 2 * M_PI / (sp->resolution - 1) + M_PI;
319 /* v ranges from PI/2 down to -PI/2 */
320 v = (1 - iv) * mode3 * M_PI / (sp->resolution - 1) + M_PI * (mode3 / 2.0);
322 /* Use of xExponent */
323 sp->se[iv] = Sine(u, sp->xExponent);
324 sp->ce[iv] = Cosine(u, sp->xExponent);
325 sp->sn[iv] = Sine(v, sp->yExponent);
326 sp->cn[iv] = Cosine(v, sp->yExponent) * inverter2 + cn3;
328 /* Normal vector computations only */
329 sp->sw[iv] = Sine(u + flatu, 2 - sp->xExponent);
330 sp->cw[iv] = Cosine(u + flatu, 2 - sp->xExponent);
331 sp->ss[iv] = Sine(v + flatv, 2 - sp->yExponent) * inverter2;
332 sp->cs[iv] = Cosine(v + flatv, 2 - sp->yExponent);
335 /* Now fix up the endpoints */
336 sp->se[sp->resolution] = sp->se[1];
337 sp->ce[sp->resolution] = sp->ce[1];
339 if (sp->Mode > 2.999999) {
340 sp->sn[sp->resolution] = sp->sn[1];
341 sp->cn[sp->resolution] = sp->cn[1];
347 DoneScale(superquadricsstruct * sp)
349 double xx, yy, zz, xp = 0, yp = 0, zp = 0, xn, yn, zn, xnp = 0,
353 /* Hey don't knock my 2-letter variable names. Simon's BASIC rules, man! ;-> */
354 /* Just kidding..... */
357 for (ih = 1; ih <= sp->resolution; ih++) {
359 for (iv = 1; iv <= sp->resolution; iv++) {
362 glColor3f(sp->curmat[toggle][0], sp->curmat[toggle][1], sp->curmat[toggle][2]);
364 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, sp->curmat[toggle]);
366 xx = sp->cn[iv] * sp->ce[ih];
367 zz = sp->cn[iv] * sp->se[ih];
371 if ((ih > 1) || (iv > 1)) {
374 glVertex3f(xx, yy, zz);
375 glVertex3f(sp->Prevxx[iv], sp->Prevyy[iv], sp->Prevzz[iv]);
378 glVertex3f(xx, yy, zz);
379 glVertex3f(sp->Prevxx[iv - 1], sp->Prevyy[iv - 1], sp->Prevzz[iv - 1]);
381 /* PURIFY 4.0.1 reports an unitialized memory read on the next line when using
382 * MesaGL 2.2 and -mono. This has been fixed in MesaGL 2.3 and later. */
386 if ((sp->cs[iv] > 1e+10) || (sp->cs[iv] < -1e+10)) {
391 xn = sp->cs[iv] * sp->cw[ih];
392 zn = sp->cs[iv] * sp->sw[ih];
395 if ((ih > 1) && (iv > 1)) {
396 glNormal3f(xn, yn, zn);
398 glVertex3f(xx, yy, zz);
400 glNormal3f(sp->Prevxn[iv], sp->Prevyn[iv], sp->Prevzn[iv]);
401 glVertex3f(sp->Prevxx[iv], sp->Prevyy[iv], sp->Prevzz[iv]);
403 glNormal3f(xnp, ynp, znp);
404 glVertex3f(xp, yp, zp);
406 glNormal3f(sp->Prevxn[iv - 1], sp->Prevyn[iv - 1], sp->Prevzn[iv - 1]);
407 glVertex3f(sp->Prevxx[iv - 1], sp->Prevyy[iv - 1], sp->Prevzz[iv - 1]);
412 glShadeModel(GL_FLAT);
413 glDisable(GL_LIGHTING);
415 glVertex3f(xx, yy, zz);
416 glVertex3f(xx + xn, yy + yn, zz + zn);
419 glShadeModel(GL_SMOOTH);
420 glEnable(GL_LIGHTING);
422 xnp = sp->Prevxn[iv];
423 ynp = sp->Prevyn[iv];
424 znp = sp->Prevzn[iv];
441 /**** End of really old code ****/
444 SetCull(int init, superquadricsstruct * sp)
452 if (sp->Mode < 1.0001) {
454 glEnable(GL_CULL_FACE);
458 } else if (sp->Mode > 2.9999) {
460 glEnable(GL_CULL_FACE);
461 glCullFace(GL_FRONT);
466 glDisable(GL_CULL_FACE);
473 SetCurrentShape(superquadricsstruct * sp)
477 sp->xExponent = sp->now.xExponent = sp->later.xExponent;
478 sp->yExponent = sp->now.yExponent = sp->later.yExponent;
480 for (t = 0; t < 4; ++t) {
481 sp->curmat[t][0] = sp->now.r[t] = sp->later.r[t];
482 sp->curmat[t][1] = sp->now.g[t] = sp->later.g[t];
483 sp->curmat[t][2] = sp->now.b[t] = sp->later.b[t];
486 sp->Mode = (double) (sp->now.Mode = sp->later.Mode);
487 sp->rotx = sp->now.rotx = sp->later.rotx;
488 sp->rotz = sp->now.rotz = sp->later.rotz;
490 sp->counter = -sp->maxwait;
496 NextSuperquadric(superquadricsstruct * sp)
501 sp->roty -= sp->spinspeed;
502 while (sp->roty >= 360.0)
504 while (sp->roty < 0.0)
509 if (sp->counter > 0) {
510 if (--sp->counter == 0) {
512 if (sp->counter == 0) { /* Happens if sp->maxwait == 0 */
514 sp->counter = sp->maxcount;
517 fnow = (double) sp->counter / (double) sp->maxcount;
518 flater = (double) (sp->maxcount - sp->counter) / (double) sp->maxcount;
519 sp->xExponent = sp->now.xExponent * fnow + sp->later.xExponent * flater;
520 sp->yExponent = sp->now.yExponent * fnow + sp->later.yExponent * flater;
522 for (t = 0; t < 4; ++t) {
523 sp->curmat[t][0] = sp->now.r[t] * fnow + sp->later.r[t] * flater;
524 sp->curmat[t][1] = sp->now.g[t] * fnow + sp->later.g[t] * flater;
525 sp->curmat[t][2] = sp->now.b[t] * fnow + sp->later.b[t] * flater;
528 sp->Mode = (double) sp->now.Mode * fnow + (double) sp->later.Mode * flater;
529 sp->rotx = (double) sp->now.rotx * fnow + (double) sp->later.rotx * flater;
530 sp->rotz = (double) sp->now.rotz * fnow + (double) sp->later.rotz * flater;
535 if (++sp->counter >= 0) {
537 sp->counter = sp->maxcount;
543 DisplaySuperquadrics(superquadricsstruct * sp)
545 glDrawBuffer(GL_BACK);
547 glClear(GL_COLOR_BUFFER_BIT);
549 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
551 if (sp->viewcount < 1) {
552 sp->viewcount = sp->viewwait;
553 ReshapeSuperquadrics(-1, -1);
556 glTranslatef(0.0, 0.0, -((GLfloat) (sp->dist) / 16.0) - (sp->Mode * 3.0 - 1.0)); /* viewing transform */
557 glRotatef(sp->rotx, 1.0, 0.0, 0.0); /* pitch */
558 glRotatef(sp->rotz, 0.0, 0.0, 1.0); /* bank */
559 glRotatef(sp->roty, 0.0, 1.0, 0.0); /* "spin", like heading but comes after P & B */
567 /* Remember to flush & swap the buffers after calling this function! */
571 NextSuperquadricDisplay(superquadricsstruct * sp)
573 NextSuperquadric(sp);
574 DisplaySuperquadrics(sp);
579 ReshapeSuperquadrics(int w, int h)
581 static int last_w = 0, last_h = 0;
582 int maxsize, cursize;
591 maxsize = (w < h) ? w : h;
592 if (maxsize <= MINSIZE) {
595 cursize = myrand(maxsize - MINSIZE) + MINSIZE;
597 if ((w > cursize) && (h > cursize)) {
598 glViewport(myrand(w - cursize), myrand(h - cursize), cursize, cursize);
601 glViewport(0, 0, w, h);
603 glMatrixMode(GL_PROJECTION);
605 gluPerspective(30.0, (GLfloat) w / (GLfloat) h, 0.1, 200.0);
606 glMatrixMode(GL_MODELVIEW);
611 InitSuperquadrics(int wfmode, int snorm, int res, int count, float speed, superquadricsstruct * sp)
614 {0.4, 0.4, 0.4, 1.0};
616 {10.0, 1.0, 1.0, 10.0};
617 GLfloat mat_diffuse[] =
618 {1.0, 0.5, 0.5, 1.0};
619 GLfloat mat_specular[] =
620 {0.8, 0.8, 0.8, 1.0};
621 GLfloat mat_shininess[] =
626 for (t = 0; t < 4; ++t)
627 sp->curmat[t][3] = 1.0;
632 sp->dist = (16 << 3);
633 sp->wireframe = sp->flatshade = sp->shownorms = 0;
634 sp->maxcount = count;
635 if (sp->maxcount < 1)
637 sp->maxwait = sp->maxcount >> 1;
640 sp->spinspeed = speed;
641 sp->viewcount = sp->viewwait = (sp->maxcount < 2) ? 1 : (sp->maxcount << 3);
647 sp->resolution = res;
658 glShadeModel(GL_FLAT);
659 glDisable(GL_LIGHTING);
660 glColor3f(mat_diffuse[0], mat_diffuse[1], mat_diffuse[2]);
663 glShadeModel(GL_FLAT);
667 glEnable(GL_LIGHTING);
669 glDepthFunc(GL_LEQUAL);
670 glEnable(GL_DEPTH_TEST);
672 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
673 glLightfv(GL_LIGHT0, GL_POSITION, position);
675 /*glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse); */
676 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
677 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
679 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
682 glEnable(GL_NORMALIZE);
687 MakeUpStuff(1, sp); /* Initialize it */
688 sp->counter = sp->maxcount;
691 /* End of superquadrics main functions */
694 init_superquadrics(ModeInfo * mi)
696 Display *display = MI_DISPLAY(mi);
697 Window window = MI_WINDOW(mi);
698 int screen = MI_SCREEN(mi);
700 superquadricsstruct *sp;
702 if (superquadrics == NULL) {
703 if ((superquadrics = (superquadricsstruct *) calloc(MI_NUM_SCREENS(mi),
704 sizeof (superquadricsstruct))) == NULL)
707 sp = &superquadrics[screen];
708 sp->mono = (MI_WIN_IS_MONO(mi) ? 1 : 0);
710 sp->glx_context = init_GL(mi);
712 InitSuperquadrics(MI_WIN_IS_WIREFRAME(mi) || sp->mono, 0,
713 MI_BATCHCOUNT(mi), MI_CYCLES(mi), spinspeed, sp);
714 ReshapeSuperquadrics(MI_WIN_WIDTH(mi), MI_WIN_HEIGHT(mi));
716 DisplaySuperquadrics(sp);
718 glXSwapBuffers(display, window);
722 refresh_superquadrics(ModeInfo * mi)
724 /* Nothing happens here */
728 draw_superquadrics(ModeInfo * mi)
730 superquadricsstruct *sp = &superquadrics[MI_SCREEN(mi)];
731 Display *display = MI_DISPLAY(mi);
732 Window window = MI_WINDOW(mi);
734 glXMakeCurrent(display, window, sp->glx_context);
736 NextSuperquadricDisplay(sp);
739 glXSwapBuffers(display, window);
743 release_superquadrics(ModeInfo * mi)
745 if (superquadrics != NULL) {
747 /* Don't destroy the glXContext. init_GL does that. */
749 (void) free((void *) superquadrics);
750 superquadrics = NULL;
757 /* End of superquadrics.c */