
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)superquadrics.c	4.0 97/03/30 xlockmore";

#endif

/*-
 * Superquadrics were invented by Dr. Alan Barr of Caltech University.
 * They were first published in "Computer Graphics and Applications",
 * volume 1, number 1, 1981, in the article "Superquadrics and Angle-
 * Preserving Transformations."  Dr. Barr based the Superquadrics on
 * Piet Hein's "super ellipses."  Super ellipses are like 2D ellipses,
 * except that the formula includes an exponent, raising its X and Y
 * values to a (fractional) power, and allowing them to gradually
 * change from round to square edges.  Superquadrics extend this
 * idea into 3 dimensions, using two exponents to modify a
 * quadric surface in a similar fashion.
 */

/*-
 * superquadrics.c - 3D mathematical shapes
 *
 * See xlock.c for copying information.
 *
 * Revision History:
 * 30-Mar-97: Turned into a module for xlockmore 4.02 alpha.  The code
 *    is almost unrecognizable now from the first revision, except for
 *    a few remaining two-letter variable names.  I still don't have
 *    the normal vectors working right (I wrote the buggy normal vector
 *    code myself, can you tell?)
 * 07-Jan-97: A legend reborn; Superquadrics make an appearance as a
 *    real OpenGL program written in C.  I can even render them with
 *    proper lighting and specular highlights.  Gee, they look almost
 *    as good now as the original color plates of them that my uncle
 *    showed me as a child in 1981.  I don't know what computer hardware
 *    he was using at the time, but it's taken a couple decades for the
 *    PC clone hardware to catch up to it.
 * 05-Jan-97: After almost a decade, Superquadrics had almost faded away
 *    into the myths and folklore of all the things my brother and I played
 *    with on computers when we were kids with too much time on our hands.
 *    I had since gotten involved in Unix, OpenGL, and other things.
 *    A sudden flash of inspiration caused me to dig out the old Pascal
 *    source code, run it through p2c, and start ripping away the old
 *    wireframe rendering code, to be replaced by OpenGL.
 * Late 1989 or early 1990:  Around this time I did the Turbo Pascal
 *    port of the Superquadrics.  Unfortunately, many of the original variable
 *    names remained the same from the C= 64 original.  This was unfortunate
 *    because BASIC on the c64 only allowed 2-letter, global variable names.
 *    But the speed improvement over BASIC was very impressive at the time.
 * Thanksgiving, 1987: Written.  My uncle Al, who invented Superquadrics some
 *    years earlier, came to visit us.  I was a high school kid at the time,
 *    with nothing more than a Commodore 64.  Somehow we wrote this program,
 *    (he did the math obviously, I just coded it into BASIC for the c64).
 *    Yeah, 320x200 resolution, colorless white wireframe, and half an hour
 *    rendering time per superquadric.  PLOT x,y.  THOSE were the days.
 *    In the following years I would port Superquadrics to AppleBASIC,
 *    AmigaBASIC, and then Turbo Pascal for IBM clones.  5 minutes on a 286!
 *    Talk about fast rendering!  But these days, when my Pentium 166 runs
 *    the same program, the superquadric will already be waiting on the
 *    screen before my monitor can change frequency from text to graphics
 *    mode.  Can't time the number of minutes that way!  Darn ;)
 *
 * Ed Mackey
 */

#include <X11/Intrinsic.h>
#include "xlock.h"

#ifdef USE_GL
#include <GL/xmesa.h>		/* <-- This include might not be needed */
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>

/*-
 * Note for low-CPU-speed machines:  If your frame rate is so low that
 * attempts at animation appear futile, try using "-cycles 1", which puts
 * Superquadrics into kind of a slide-show mode.  It will still use up
 * all of your CPU power, but it may look nicer.
 */

#define DEF_SPINSPEED  "5.0"

static float spinspeed;

static XrmOptionDescRec opts[] =
{
  {"-spinspeed", ".superquadrics.spinspeed", XrmoptionSepArg, (caddr_t) NULL}
};
static argtype vars[] =
{
  {(caddr_t *) & spinspeed, "spinspeed", "Spinspeed", DEF_SPINSPEED, t_Float}
};
static OptionStruct desc[] =
{
	{"-spinspeed num", "speed of rotation, in degrees per frame"}
};

ModeSpecOpt superquadrics_opts =
{1, opts, 1, vars, desc};


#define MaxRes          50
#define MinRes          5

typedef double dimi[MaxRes + 1];

typedef struct {
	double      xExponent, yExponent;
	GLfloat     r[4], g[4], b[4];
	long        Mode;
	int         rotx, rotz;
} state;

typedef struct {
	GLXContext  glx_context;
	int         dist, wireframe, flatshade, shownorms, maxcount, maxwait;
	int         counter, viewcount, viewwait, mono;
	GLfloat     curmat[4][4], rotx, roty, rotz, spinspeed;
	/* In dimi: the first letter stands for cosine/sine, the second
	 *          stands for North, South, East, or West.  I think.
	 */
	dimi        cs, se, sw, sn, ss, ce, cw, cn, Prevxx, Prevyy, Prevzz,
	            Prevxn, Prevyn, Prevzn;
	double      xExponent, yExponent, Mode;
	int         resolution;
	state       now, later;
} superquadricsstruct;

static superquadricsstruct *superquadrics = NULL;

#define CLIP_NORMALS 10000.0

void        ReshapeSuperquadrics(int w, int h);

static int
myrand(int range)
{
	return ((int) (((float) range) * LRAND() / (MAXRAND)));
}

static float
myrandreal(void)
{
	return (LRAND() / (MAXRAND));
}

/* Some old, old, OLD code follows.  Ahh this takes me back..... */

/* Output from p2c, the Pascal-to-C translator */
/* From input file "squad.pas" */

static double
XtoY(double x, double y)
{
	double      z, a;

	/* This is NOT your typical raise-X-to-the-Y-power function.  Do not attempt
	 * to replace this with a standard exponent function.  If you must, just
	 * replace the "a = exp(y * log(z));" line with something faster.
	 */

	z = fabs(x);
	if (z < 1e-20) {
		a = 0.0;
		return a;
	}
	a = exp(y * log(z));
	if (a > CLIP_NORMALS)
		a = CLIP_NORMALS;
	if (x < 0)
		a = -a;
	return a;
}


static double
Sine(double x, double e)
{
	/* This is just the sine wave raised to the exponent.  BUT, you can't
	 * raise negative numbers to fractional exponents.  So we have a special
	 * XtoY routune which handles it in a way useful to superquadrics.
	 */

	return (XtoY(sin(x), e));
}


static double
Cosine(double x, double e)
{
	return (XtoY(cos(x), e));
}


static void
MakeUpStuff(int allstuff, superquadricsstruct * sp)
{
	static int  pats[4][4] =
	{
		{0, 0, 0, 0},
		{0, 1, 0, 1},
		{0, 0, 1, 1},
		{0, 1, 1, 0}
	};

	int         dostuff;
	int         t, pat;
	GLfloat     r, g, b, r2, g2, b2;

	/* randomize it. */

	if (sp->maxcount < 2)
		allstuff = 1;
	dostuff = allstuff * 15;
	if (!dostuff) {
		dostuff = myrand(3) + 1;
		if (myrand(2) || (dostuff & 1))
			dostuff |= 4;
		if (myrand(2))
			dostuff |= 8;
	}
	if (dostuff & 1) {
		sp->later.xExponent = (((long) floor(myrandreal() * 250 + 0.5)) / 100.0) + 0.1;
		sp->later.yExponent = (((long) floor(myrandreal() * 250 + 0.5)) / 100.0) + 0.1;

		/* Increase the 2.0 .. 2.5 range to 2.0 .. 3.0 */
		if (sp->later.xExponent > 2.0)
			sp->later.xExponent = (sp->later.xExponent * 2.0) - 2.0;
		if (sp->later.yExponent > 2.0)
			sp->later.yExponent = (sp->later.yExponent * 2.0) - 2.0;
	}
	if (dostuff & 2) {
		do {
			sp->later.Mode = myrand(3L) + 1;
		} while (!allstuff && (sp->later.Mode == sp->now.Mode));
		/* On init: make sure it can stay in mode 1 if it feels like it. */
	}
	if (dostuff & 4) {
		if (sp->mono) {
			b = g = r = (GLfloat) (140 + myrand(100)) / 255.0;
			b2 = g2 = r2 = ((r > 0.69) ? (1.0 - r) : r);
		} else {
			r = (GLfloat) (40 + myrand(200)) / 255.0;
			g = (GLfloat) (40 + myrand(200)) / 255.0;
			b = (GLfloat) (40 + myrand(200)) / 255.0;

			r2 = ((myrand(4) && ((r < 0.31) || (r > 0.69))) ? (1.0 - r) : r);
			g2 = ((myrand(4) && ((g < 0.31) || (g > 0.69))) ? (1.0 - g) : g);
			b2 = ((myrand(4) && ((b < 0.31) || (b > 0.69))) ? (1.0 - b) : b);
		}

		pat = myrand(4);
		for (t = 0; t < 4; ++t) {
			sp->later.r[t] = pats[pat][t] ? r : r2;
			sp->later.g[t] = pats[pat][t] ? g : g2;
			sp->later.b[t] = pats[pat][t] ? b : b2;
		}
	}
	if (dostuff & 8) {
		sp->later.rotx = myrand(360) - 180;
		sp->later.rotz = myrand(160) - 80;
	}
}

static void
inputs(superquadricsstruct * sp)
{
	int         iv;
	double      u, v, mode3, cn3, inverter2, flatu, flatv;

	if (sp->Mode < 1.000001) {
		mode3 = 1.0;
		cn3 = 0.0;
		inverter2 = 1.0;
	} else if (sp->Mode < 2.000001) {
		mode3 = 1.0;
		cn3 = (sp->Mode - 1.0) * 1.5;
		inverter2 = (sp->Mode - 1.0) * -2.0 + 1.0;
	} else {
		mode3 = (sp->Mode - 1.0);
		cn3 = (sp->Mode - 2.0) / 2.0 + 1.5;
		inverter2 = -1.0;
	}

	if (sp->flatshade) {
		flatu = M_PI / (sp->resolution - 1);
		flatv = mode3 * M_PI / ((sp->resolution - 1) * 2);
	} else {
		flatu = flatv = 0.0;
	}

	/* (void) printf("Calculating....\n"); */
	for (iv = 1; iv <= sp->resolution; iv++) {

		/* u ranges from PI down to -PI */
		u = (1 - iv) * 2 * M_PI / (sp->resolution - 1) + M_PI;

		/* v ranges from PI/2 down to -PI/2 */
		v = (1 - iv) * mode3 * M_PI / (sp->resolution - 1) + M_PI * (mode3 / 2.0);

		/* Use of xExponent */
		sp->se[iv] = Sine(u, sp->xExponent);
		sp->ce[iv] = Cosine(u, sp->xExponent);
		sp->sn[iv] = Sine(v, sp->yExponent);
		sp->cn[iv] = Cosine(v, sp->yExponent) * inverter2 + cn3;

		/* Normal vector computations only */
		sp->sw[iv] = Sine(u + flatu, 2 - sp->xExponent);
		sp->cw[iv] = Cosine(u + flatu, 2 - sp->xExponent);
		sp->ss[iv] = Sine(v + flatv, 2 - sp->yExponent) * inverter2;
		sp->cs[iv] = Cosine(v + flatv, 2 - sp->yExponent);
	}			/* next */

	/* Now fix up the endpoints */
	sp->se[sp->resolution] = sp->se[1];
	sp->ce[sp->resolution] = sp->ce[1];

	if (sp->Mode > 2.999999) {
		sp->sn[sp->resolution] = sp->sn[1];
		sp->cn[sp->resolution] = sp->cn[1];
	}
}


static void
DoneScale(superquadricsstruct * sp)
{
	double      xx, yy, zz, xp = 0, yp = 0, zp = 0, xn, yn, zn, xnp = 0,
	            ynp = 0, znp = 0;
	int         ih, iv;

	/* Hey don't knock my 2-letter variable names.  Simon's BASIC rules, man! ;-> */
	/* Just kidding..... */
	int         toggle = 0;

	for (ih = 1; ih <= sp->resolution; ih++) {
		toggle ^= 2;
		for (iv = 1; iv <= sp->resolution; iv++) {
			toggle ^= 1;
			if (sp->wireframe)
				glColor3f(sp->curmat[toggle][0], sp->curmat[toggle][1], sp->curmat[toggle][2]);
			else
				glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, sp->curmat[toggle]);

			xx = sp->cn[iv] * sp->ce[ih];
			zz = sp->cn[iv] * sp->se[ih];
			yy = sp->sn[iv];

			if (sp->wireframe) {
				if ((ih > 1) || (iv > 1)) {
					glBegin(GL_LINES);
					if (ih > 1) {
						glVertex3f(xx, yy, zz);
						glVertex3f(sp->Prevxx[iv], sp->Prevyy[iv], sp->Prevzz[iv]);
					}
					if (iv > 1) {
						glVertex3f(xx, yy, zz);
						glVertex3f(sp->Prevxx[iv - 1], sp->Prevyy[iv - 1], sp->Prevzz[iv - 1]);
					}
					glEnd();
				}
			} else {
				if ((sp->cs[iv] > 1e+10) || (sp->cs[iv] < -1e+10)) {
					xn = sp->cs[iv];
					zn = sp->cs[iv];
					yn = sp->ss[iv];
				} else {
					xn = sp->cs[iv] * sp->cw[ih];
					zn = sp->cs[iv] * sp->sw[ih];
					yn = sp->ss[iv];
				}
				if ((ih > 1) && (iv > 1)) {
					glNormal3f(xn, yn, zn);
					glBegin(GL_POLYGON);
					glVertex3f(xx, yy, zz);
					if (!sp->flatshade)
						glNormal3f(sp->Prevxn[iv], sp->Prevyn[iv], sp->Prevzn[iv]);
					glVertex3f(sp->Prevxx[iv], sp->Prevyy[iv], sp->Prevzz[iv]);
					if (!sp->flatshade)
						glNormal3f(xnp, ynp, znp);
					glVertex3f(xp, yp, zp);
					if (!sp->flatshade)
						glNormal3f(sp->Prevxn[iv - 1], sp->Prevyn[iv - 1], sp->Prevzn[iv - 1]);
					glVertex3f(sp->Prevxx[iv - 1], sp->Prevyy[iv - 1], sp->Prevzz[iv - 1]);
					glEnd();
				}
				if (sp->shownorms) {
					if (!sp->flatshade)
						glShadeModel(GL_FLAT);
					glDisable(GL_LIGHTING);
					glBegin(GL_LINES);
					glVertex3f(xx, yy, zz);
					glVertex3f(xx + xn, yy + yn, zz + zn);
					glEnd();
					if (!sp->flatshade)
						glShadeModel(GL_SMOOTH);
					glEnable(GL_LIGHTING);
				}
				xnp = sp->Prevxn[iv];
				ynp = sp->Prevyn[iv];
				znp = sp->Prevzn[iv];
				sp->Prevxn[iv] = xn;
				sp->Prevyn[iv] = yn;
				sp->Prevzn[iv] = zn;
			}

			xp = sp->Prevxx[iv];
			yp = sp->Prevyy[iv];
			zp = sp->Prevzz[iv];
			sp->Prevxx[iv] = xx;
			sp->Prevyy[iv] = yy;
			sp->Prevzz[iv] = zz;

		}		/* next */
	}			/* next */
}

/**** End of really old code ****/

void
SetCull(int init, superquadricsstruct * sp)
{
	static int  cullmode;

	if (init) {
		cullmode = 0;
		return;
	}
	if (sp->Mode < 1.0001) {
		if (cullmode != 1) {
			glEnable(GL_CULL_FACE);
			glCullFace(GL_BACK);
			cullmode = 1;
		}
	} else if (sp->Mode > 2.9999) {
		if (cullmode != 2) {
			glEnable(GL_CULL_FACE);
			glCullFace(GL_FRONT);
			cullmode = 2;
		}
	} else {
		if (cullmode) {
			glDisable(GL_CULL_FACE);
			cullmode = 0;
		}
	}
}

void
SetCurrentShape(superquadricsstruct * sp)
{
	int         t;

	sp->xExponent = sp->now.xExponent = sp->later.xExponent;
	sp->yExponent = sp->now.yExponent = sp->later.yExponent;

	for (t = 0; t < 4; ++t) {
		sp->curmat[t][0] = sp->now.r[t] = sp->later.r[t];
		sp->curmat[t][1] = sp->now.g[t] = sp->later.g[t];
		sp->curmat[t][2] = sp->now.b[t] = sp->later.b[t];
	}

	sp->Mode = (double) (sp->now.Mode = sp->later.Mode);
	sp->rotx = sp->now.rotx = sp->later.rotx;
	sp->rotz = sp->now.rotz = sp->later.rotz;

	sp->counter = -sp->maxwait;

	inputs(sp);
}

void
NextSuperquadric(superquadricsstruct * sp)
{
	double      fnow, flater;
	int         t;

	sp->roty -= sp->spinspeed;
	while (sp->roty >= 360.0)
		sp->roty -= 360.0;
	while (sp->roty < 0.0)
		sp->roty += 360.0;

	--sp->viewcount;

	if (sp->counter > 0) {
		if (--sp->counter == 0) {
			SetCurrentShape(sp);
			if (sp->counter == 0) {		/* Happens if sp->maxwait == 0 */
				MakeUpStuff(0, sp);
				sp->counter = sp->maxcount;
			}
		} else {
			fnow = (double) sp->counter / (double) sp->maxcount;
			flater = (double) (sp->maxcount - sp->counter) / (double) sp->maxcount;
			sp->xExponent = sp->now.xExponent * fnow + sp->later.xExponent * flater;
			sp->yExponent = sp->now.yExponent * fnow + sp->later.yExponent * flater;

			for (t = 0; t < 4; ++t) {
				sp->curmat[t][0] = sp->now.r[t] * fnow + sp->later.r[t] * flater;
				sp->curmat[t][1] = sp->now.g[t] * fnow + sp->later.g[t] * flater;
				sp->curmat[t][2] = sp->now.b[t] * fnow + sp->later.b[t] * flater;
			}

			sp->Mode = (double) sp->now.Mode * fnow + (double) sp->later.Mode * flater;
			sp->rotx = (double) sp->now.rotx * fnow + (double) sp->later.rotx * flater;
			sp->rotz = (double) sp->now.rotz * fnow + (double) sp->later.rotz * flater;

			inputs(sp);
		}
	} else {
		if (++sp->counter >= 0) {
			MakeUpStuff(0, sp);
			sp->counter = sp->maxcount;
		}
	}
}

void
DisplaySuperquadrics(superquadricsstruct * sp)
{
	if (sp->wireframe)
		glClear(GL_COLOR_BUFFER_BIT);
	else
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	if (sp->viewcount < 1) {
		sp->viewcount = sp->viewwait;
		ReshapeSuperquadrics(-1, -1);
	}
	glPushMatrix();
	glTranslatef(0.0, 0.0, -((GLfloat) (sp->dist) / 16.0) - (sp->Mode * 3.0 - 1.0));	/* viewing transform  */
	glRotatef(sp->rotx, 1.0, 0.0, 0.0);	/* pitch */
	glRotatef(sp->rotz, 0.0, 0.0, 1.0);	/* bank */
	glRotatef(sp->roty, 0.0, 1.0, 0.0);	/* "spin", like heading but comes after P & B */

	SetCull(0, sp);

	DoneScale(sp);

	glPopMatrix();

	/* Remember to flush & swap the buffers after calling this function! */
}

void
NextSuperquadricDisplay(superquadricsstruct * sp)
{
	NextSuperquadric(sp);
	DisplaySuperquadrics(sp);
}

#define MINSIZE 200
void
ReshapeSuperquadrics(int w, int h)
{
	static int  last_w = 0, last_h = 0;
	int         maxsize, cursize;

	if (w < 0) {
		w = last_w;
		h = last_h;
	} else {
		last_w = w;
		last_h = h;
	}
	maxsize = (w < h) ? w : h;
	if (maxsize <= MINSIZE) {
		cursize = maxsize;
	} else {
		cursize = myrand(maxsize - MINSIZE) + MINSIZE;
	}
	if ((w > cursize) && (h > cursize)) {
		glViewport(myrand(w - cursize), myrand(h - cursize), cursize, cursize);
		w = h = cursize;
	} else {
		glViewport(0, 0, w, h);
	}
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(30.0, (GLfloat) w / (GLfloat) h, 0.1, 200.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void
InitSuperquadrics(int wfmode, int snorm, int res, int count, float speed, superquadricsstruct * sp)
{
	GLfloat     ambient[] =
	{0.4, 0.4, 0.4, 1.0};
	GLfloat     position[] =
	{10.0, 1.0, 1.0, 10.0};
	GLfloat     mat_diffuse[] =
	{1.0, 0.5, 0.5, 1.0};
	GLfloat     mat_specular[] =
	{0.8, 0.8, 0.8, 1.0};
	GLfloat     mat_shininess[] =
	{50.0};

	int         t;

	for (t = 0; t < 4; ++t)
		sp->curmat[t][3] = 1.0;

	sp->rotx = 35.0;
	sp->roty = 0.0;
	sp->rotz = 0.0;
	sp->dist = (16 << 3);
	sp->wireframe = sp->flatshade = sp->shownorms = 0;
	sp->maxcount = count;
	if (sp->maxcount < 1)
		sp->maxcount = 1;
	sp->maxwait = sp->maxcount >> 1;
	SetCull(1, sp);

	sp->spinspeed = speed;
	sp->viewcount = sp->viewwait = (sp->maxcount < 2) ? 1 : (sp->maxcount << 3);

	if (res < MinRes)
		res = MinRes;
	if (res > MaxRes)
		res = MaxRes;
	sp->resolution = res;

	if (wfmode == 2)
		sp->flatshade = 1;
	else if (wfmode)
		sp->wireframe = 1;

	if (snorm)
		sp->shownorms = 1;

	if (sp->wireframe) {
		glShadeModel(GL_FLAT);
		glDisable(GL_LIGHTING);
		glColor3f(mat_diffuse[0], mat_diffuse[1], mat_diffuse[2]);
	} else {
		if (sp->flatshade) {
			glShadeModel(GL_FLAT);
			position[0] = 1.0;
			position[3] = 0.0;
		}
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		glDepthFunc(GL_LEQUAL);
		glEnable(GL_DEPTH_TEST);

		glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
		glLightfv(GL_LIGHT0, GL_POSITION, position);

		/*glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse); */
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

		glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

		glFrontFace(GL_CW);
		glEnable(GL_NORMALIZE);
	}

	MakeUpStuff(1, sp);
	SetCurrentShape(sp);
	MakeUpStuff(1, sp);	/* Initialize it */
	sp->counter = sp->maxcount;
}

/* End of superquadrics main functions */

void
init_superquadrics(ModeInfo * mi)
{
	Display    *display = MI_DISPLAY(mi);
	Window      window = MI_WINDOW(mi);
	int         screen = MI_SCREEN(mi);

	superquadricsstruct *sp;

	if (superquadrics == NULL) {
		if ((superquadrics = (superquadricsstruct *) calloc(MI_NUM_SCREENS(mi),
				      sizeof (superquadricsstruct))) == NULL)
			return;
	}
	sp = &superquadrics[screen];

	sp->mono = (MI_WIN_IS_MONO(mi) ? 1 : 0);

	if (sp->glx_context) {
		glXDestroyContext(display, sp->glx_context);
		sp->glx_context = NULL;
	}

	{
		int         n;
		XVisualInfo *wantVis, vTemplate;
		extern int  VisualClassWanted;

		vTemplate.screen = screen;
		vTemplate.depth = MI_WIN_DEPTH(mi);
		if (VisualClassWanted == -1) {
			vTemplate.class = DefaultVisual(display, screen)->class;
		} else {
			vTemplate.class = VisualClassWanted;
		}
		wantVis = XGetVisualInfo(display,
			VisualScreenMask | VisualDepthMask | VisualClassMask,
					 &vTemplate, &n);
		if (VisualClassWanted != -1 && n == 0) {
			/* Wanted visual not found so use default */
			vTemplate.class = DefaultVisual(display, screen)->class;
			wantVis = XGetVisualInfo(display,
			VisualScreenMask | VisualDepthMask | VisualClassMask,
						 &vTemplate, &n);
		}
		/* if User asked for color, try that first, then try mono */
		/* if User asked for mono.  Might fail on 16/24 bit displays,
		   so fall back on color, but keep the mono "look & feel". */
		if (!getVisual(mi, wantVis, sp->mono)) {
			if (!getVisual(mi, wantVis, !sp->mono)) {
				(void) fprintf(stderr, "GL can not render with root visual\n");
				return;
			}
		}
		if (MI_WIN_IS_VERBOSE(mi))
			showVisualInfo(wantVis);
/* PURIFY 3.0a on SunOS4 reports a 104 byte memory leak on the next line each 
 * time that superquadrics mode is run in random mode. */ 
		sp->glx_context = glXCreateContext(display, wantVis, 0, True);
		XFree((char *) wantVis);

	}

	glXMakeCurrent(display, window, sp->glx_context);
	ReshapeSuperquadrics(MI_WIN_WIDTH(mi), MI_WIN_HEIGHT(mi));
	if (sp->mono) {
		glIndexi(WhitePixel(display, screen));
		glClearIndex(BlackPixel(display, screen));
	}
	InitSuperquadrics(MI_WIN_IS_WIREFRAME(mi) || sp->mono, 0,
		MI_BATCHCOUNT(mi), MI_CYCLES(mi), spinspeed, sp);

	DisplaySuperquadrics(sp);
	glFinish();
	glXSwapBuffers(display, window);
}

void
            refresh_superquadrics(ModeInfo * mi) {
	/* Nothing happens here */
}

void
            draw_superquadrics(ModeInfo * mi) {
	superquadricsstruct *sp = &superquadrics[MI_SCREEN(mi)];
	Display *   display = MI_DISPLAY(mi);
	Window      window = MI_WINDOW(mi);

	glXMakeCurrent(display, window, sp->glx_context);

	NextSuperquadricDisplay(sp);

	glFinish();
	glXSwapBuffers(display, window);
}

void
            release_superquadrics(ModeInfo * mi) {
	if (superquadrics != NULL) {
		int         screen;

		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
			superquadricsstruct *sp = &superquadrics[screen];

			if (sp->glx_context)
				glXDestroyContext(MI_DISPLAY(mi), sp->glx_context);
#if 0 /* This is wrong for multiscreens anyway */
#ifdef GLX_MESA_release_buffers
			glXReleaseBuffersMESA(MI_DISPLAY(mi), MI_WINDOW(mi));
#endif
#endif
		}
		(void) free((void *) superquadrics);
		superquadrics = NULL;
	}
}


#endif

/* End of superquadrics.c */
