1/*
2 * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
3 * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice including the dates of first publication and
13 * either this permission notice or a reference to
14 * http://oss.sgi.com/projects/FreeB/
15 * shall be included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
22 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Except as contained in this notice, the name of Silicon Graphics, Inc.
26 * shall not be used in advertising or otherwise to promote the sale, use or
27 * other dealings in this Software without prior written authorization from
28 * Silicon Graphics, Inc.
29 */
30
31#include "gluos.h"
32#include "gluint.h"
33#include <stdio.h>
34#include <stdlib.h>
35#include <math.h>
36#include <GL/gl.h>
37#include <GL/glu.h>
38
39/* Make it not a power of two to avoid cache thrashing on the chip */
40#define CACHE_SIZE	240
41
42#undef	PI
43#define PI	      3.14159265358979323846
44
45struct GLUquadric {
46    GLint	normals;
47    GLboolean	textureCoords;
48    GLint	orientation;
49    GLint	drawStyle;
50    void	(GLAPIENTRY *errorCallback)( GLint );
51};
52
53GLUquadric * GLAPIENTRY
54gluNewQuadric(void)
55{
56    GLUquadric *newstate;
57
58    newstate = (GLUquadric *) malloc(sizeof(GLUquadric));
59    if (newstate == NULL) {
60	/* Can't report an error at this point... */
61	return NULL;
62    }
63    newstate->normals = GLU_SMOOTH;
64    newstate->textureCoords = GL_FALSE;
65    newstate->orientation = GLU_OUTSIDE;
66    newstate->drawStyle = GLU_FILL;
67    newstate->errorCallback = NULL;
68    return newstate;
69}
70
71
72void GLAPIENTRY
73gluDeleteQuadric(GLUquadric *state)
74{
75    free(state);
76}
77
78static void gluQuadricError(GLUquadric *qobj, GLenum which)
79{
80    if (qobj->errorCallback) {
81	qobj->errorCallback(which);
82    }
83}
84
85void GLAPIENTRY
86gluQuadricCallback(GLUquadric *qobj, GLenum which, _GLUfuncptr fn)
87{
88    switch (which) {
89      case GLU_ERROR:
90	qobj->errorCallback = (void (GLAPIENTRY *)(GLint)) fn;
91	break;
92      default:
93	gluQuadricError(qobj, GLU_INVALID_ENUM);
94	return;
95    }
96}
97
98void GLAPIENTRY
99gluQuadricNormals(GLUquadric *qobj, GLenum normals)
100{
101    switch (normals) {
102      case GLU_SMOOTH:
103      case GLU_FLAT:
104      case GLU_NONE:
105	break;
106      default:
107	gluQuadricError(qobj, GLU_INVALID_ENUM);
108	return;
109    }
110    qobj->normals = normals;
111}
112
113void GLAPIENTRY
114gluQuadricTexture(GLUquadric *qobj, GLboolean textureCoords)
115{
116    qobj->textureCoords = textureCoords;
117}
118
119void GLAPIENTRY
120gluQuadricOrientation(GLUquadric *qobj, GLenum orientation)
121{
122    switch(orientation) {
123      case GLU_OUTSIDE:
124      case GLU_INSIDE:
125	break;
126      default:
127	gluQuadricError(qobj, GLU_INVALID_ENUM);
128	return;
129    }
130    qobj->orientation = orientation;
131}
132
133void GLAPIENTRY
134gluQuadricDrawStyle(GLUquadric *qobj, GLenum drawStyle)
135{
136    switch(drawStyle) {
137      case GLU_POINT:
138      case GLU_LINE:
139      case GLU_FILL:
140      case GLU_SILHOUETTE:
141	break;
142      default:
143	gluQuadricError(qobj, GLU_INVALID_ENUM);
144	return;
145    }
146    qobj->drawStyle = drawStyle;
147}
148
149void GLAPIENTRY
150gluCylinder(GLUquadric *qobj, GLdouble baseRadius, GLdouble topRadius,
151		GLdouble height, GLint slices, GLint stacks)
152{
153    GLint i,j;
154    GLfloat sinCache[CACHE_SIZE];
155    GLfloat cosCache[CACHE_SIZE];
156    GLfloat sinCache2[CACHE_SIZE];
157    GLfloat cosCache2[CACHE_SIZE];
158    GLfloat sinCache3[CACHE_SIZE];
159    GLfloat cosCache3[CACHE_SIZE];
160    GLfloat angle;
161    GLfloat zLow, zHigh;
162    GLfloat sintemp, costemp;
163    GLfloat length;
164    GLfloat deltaRadius;
165    GLfloat zNormal;
166    GLfloat xyNormalRatio;
167    GLfloat radiusLow, radiusHigh;
168    int needCache2, needCache3;
169
170    if (slices >= CACHE_SIZE) slices = CACHE_SIZE-1;
171
172    if (slices < 2 || stacks < 1 || baseRadius < 0.0 || topRadius < 0.0 ||
173	    height < 0.0) {
174	gluQuadricError(qobj, GLU_INVALID_VALUE);
175	return;
176    }
177
178    /* Compute length (needed for normal calculations) */
179    deltaRadius = baseRadius - topRadius;
180    length = SQRT(deltaRadius*deltaRadius + height*height);
181    if (length == 0.0) {
182	gluQuadricError(qobj, GLU_INVALID_VALUE);
183	return;
184    }
185
186    /* Cache is the vertex locations cache */
187    /* Cache2 is the various normals at the vertices themselves */
188    /* Cache3 is the various normals for the faces */
189    needCache2 = needCache3 = 0;
190    if (qobj->normals == GLU_SMOOTH) {
191	needCache2 = 1;
192    }
193
194    if (qobj->normals == GLU_FLAT) {
195	if (qobj->drawStyle != GLU_POINT) {
196	    needCache3 = 1;
197	}
198	if (qobj->drawStyle == GLU_LINE) {
199	    needCache2 = 1;
200	}
201    }
202
203    zNormal = deltaRadius / length;
204    xyNormalRatio = height / length;
205
206    for (i = 0; i < slices; i++) {
207	angle = 2 * PI * i / slices;
208	if (needCache2) {
209	    if (qobj->orientation == GLU_OUTSIDE) {
210		sinCache2[i] = xyNormalRatio * SIN(angle);
211		cosCache2[i] = xyNormalRatio * COS(angle);
212	    } else {
213		sinCache2[i] = -xyNormalRatio * SIN(angle);
214		cosCache2[i] = -xyNormalRatio * COS(angle);
215	    }
216	}
217	sinCache[i] = SIN(angle);
218	cosCache[i] = COS(angle);
219    }
220
221    if (needCache3) {
222	for (i = 0; i < slices; i++) {
223	    angle = 2 * PI * (i-0.5) / slices;
224	    if (qobj->orientation == GLU_OUTSIDE) {
225		sinCache3[i] = xyNormalRatio * SIN(angle);
226		cosCache3[i] = xyNormalRatio * COS(angle);
227	    } else {
228		sinCache3[i] = -xyNormalRatio * SIN(angle);
229		cosCache3[i] = -xyNormalRatio * COS(angle);
230	    }
231	}
232    }
233
234    sinCache[slices] = sinCache[0];
235    cosCache[slices] = cosCache[0];
236    if (needCache2) {
237	sinCache2[slices] = sinCache2[0];
238	cosCache2[slices] = cosCache2[0];
239    }
240    if (needCache3) {
241	sinCache3[slices] = sinCache3[0];
242	cosCache3[slices] = cosCache3[0];
243    }
244
245    switch (qobj->drawStyle) {
246      case GLU_FILL:
247	/* Note:
248	** An argument could be made for using a TRIANGLE_FAN for the end
249	** of the cylinder of either radii is 0.0 (a cone).  However, a
250	** TRIANGLE_FAN would not work in smooth shading mode (the common
251	** case) because the normal for the apex is different for every
252	** triangle (and TRIANGLE_FAN doesn't let me respecify that normal).
253	** Now, my choice is GL_TRIANGLES, or leave the GL_QUAD_STRIP and
254	** just let the GL trivially reject one of the two triangles of the
255	** QUAD.  GL_QUAD_STRIP is probably faster, so I will leave this code
256	** alone.
257	*/
258	for (j = 0; j < stacks; j++) {
259	    zLow = j * height / stacks;
260	    zHigh = (j + 1) * height / stacks;
261	    radiusLow = baseRadius - deltaRadius * ((float) j / stacks);
262	    radiusHigh = baseRadius - deltaRadius * ((float) (j + 1) / stacks);
263
264	    glBegin(GL_QUAD_STRIP);
265	    for (i = 0; i <= slices; i++) {
266		switch(qobj->normals) {
267		  case GLU_FLAT:
268		    glNormal3f(sinCache3[i], cosCache3[i], zNormal);
269		    break;
270		  case GLU_SMOOTH:
271		    glNormal3f(sinCache2[i], cosCache2[i], zNormal);
272		    break;
273		  case GLU_NONE:
274		  default:
275		    break;
276		}
277		if (qobj->orientation == GLU_OUTSIDE) {
278		    if (qobj->textureCoords) {
279			glTexCoord2f(1 - (float) i / slices,
280				(float) j / stacks);
281		    }
282		    glVertex3f(radiusLow * sinCache[i],
283			    radiusLow * cosCache[i], zLow);
284		    if (qobj->textureCoords) {
285			glTexCoord2f(1 - (float) i / slices,
286				(float) (j+1) / stacks);
287		    }
288		    glVertex3f(radiusHigh * sinCache[i],
289			    radiusHigh * cosCache[i], zHigh);
290		} else {
291		    if (qobj->textureCoords) {
292			glTexCoord2f(1 - (float) i / slices,
293				(float) (j+1) / stacks);
294		    }
295		    glVertex3f(radiusHigh * sinCache[i],
296			    radiusHigh * cosCache[i], zHigh);
297		    if (qobj->textureCoords) {
298			glTexCoord2f(1 - (float) i / slices,
299				(float) j / stacks);
300		    }
301		    glVertex3f(radiusLow * sinCache[i],
302			    radiusLow * cosCache[i], zLow);
303		}
304	    }
305	    glEnd();
306	}
307	break;
308      case GLU_POINT:
309	glBegin(GL_POINTS);
310	for (i = 0; i < slices; i++) {
311	    switch(qobj->normals) {
312	      case GLU_FLAT:
313	      case GLU_SMOOTH:
314		glNormal3f(sinCache2[i], cosCache2[i], zNormal);
315		break;
316	      case GLU_NONE:
317	      default:
318		break;
319	    }
320	    sintemp = sinCache[i];
321	    costemp = cosCache[i];
322	    for (j = 0; j <= stacks; j++) {
323		zLow = j * height / stacks;
324		radiusLow = baseRadius - deltaRadius * ((float) j / stacks);
325
326		if (qobj->textureCoords) {
327		    glTexCoord2f(1 - (float) i / slices,
328			    (float) j / stacks);
329		}
330		glVertex3f(radiusLow * sintemp,
331			radiusLow * costemp, zLow);
332	    }
333	}
334	glEnd();
335	break;
336      case GLU_LINE:
337	for (j = 1; j < stacks; j++) {
338	    zLow = j * height / stacks;
339	    radiusLow = baseRadius - deltaRadius * ((float) j / stacks);
340
341	    glBegin(GL_LINE_STRIP);
342	    for (i = 0; i <= slices; i++) {
343		switch(qobj->normals) {
344		  case GLU_FLAT:
345		    glNormal3f(sinCache3[i], cosCache3[i], zNormal);
346		    break;
347		  case GLU_SMOOTH:
348		    glNormal3f(sinCache2[i], cosCache2[i], zNormal);
349		    break;
350		  case GLU_NONE:
351		  default:
352		    break;
353		}
354		if (qobj->textureCoords) {
355		    glTexCoord2f(1 - (float) i / slices,
356			    (float) j / stacks);
357		}
358		glVertex3f(radiusLow * sinCache[i],
359			radiusLow * cosCache[i], zLow);
360	    }
361	    glEnd();
362	}
363	/* Intentionally fall through here... */
364      case GLU_SILHOUETTE:
365	for (j = 0; j <= stacks; j += stacks) {
366	    zLow = j * height / stacks;
367	    radiusLow = baseRadius - deltaRadius * ((float) j / stacks);
368
369	    glBegin(GL_LINE_STRIP);
370	    for (i = 0; i <= slices; i++) {
371		switch(qobj->normals) {
372		  case GLU_FLAT:
373		    glNormal3f(sinCache3[i], cosCache3[i], zNormal);
374		    break;
375		  case GLU_SMOOTH:
376		    glNormal3f(sinCache2[i], cosCache2[i], zNormal);
377		    break;
378		  case GLU_NONE:
379		  default:
380		    break;
381		}
382		if (qobj->textureCoords) {
383		    glTexCoord2f(1 - (float) i / slices,
384			    (float) j / stacks);
385		}
386		glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i],
387			zLow);
388	    }
389	    glEnd();
390	}
391	for (i = 0; i < slices; i++) {
392	    switch(qobj->normals) {
393	      case GLU_FLAT:
394	      case GLU_SMOOTH:
395		glNormal3f(sinCache2[i], cosCache2[i], 0.0);
396		break;
397	      case GLU_NONE:
398	      default:
399		break;
400	    }
401	    sintemp = sinCache[i];
402	    costemp = cosCache[i];
403	    glBegin(GL_LINE_STRIP);
404	    for (j = 0; j <= stacks; j++) {
405		zLow = j * height / stacks;
406		radiusLow = baseRadius - deltaRadius * ((float) j / stacks);
407
408		if (qobj->textureCoords) {
409		    glTexCoord2f(1 - (float) i / slices,
410			    (float) j / stacks);
411		}
412		glVertex3f(radiusLow * sintemp,
413			radiusLow * costemp, zLow);
414	    }
415	    glEnd();
416	}
417	break;
418      default:
419	break;
420    }
421}
422
423void GLAPIENTRY
424gluDisk(GLUquadric *qobj, GLdouble innerRadius, GLdouble outerRadius,
425	    GLint slices, GLint loops)
426{
427    gluPartialDisk(qobj, innerRadius, outerRadius, slices, loops, 0.0, 360.0);
428}
429
430void GLAPIENTRY
431gluPartialDisk(GLUquadric *qobj, GLdouble innerRadius,
432		   GLdouble outerRadius, GLint slices, GLint loops,
433		   GLdouble startAngle, GLdouble sweepAngle)
434{
435    GLint i,j;
436    GLfloat sinCache[CACHE_SIZE];
437    GLfloat cosCache[CACHE_SIZE];
438    GLfloat angle;
439    GLfloat sintemp, costemp;
440    GLfloat deltaRadius;
441    GLfloat radiusLow, radiusHigh;
442    GLfloat texLow = 0.0, texHigh = 0.0;
443    GLfloat angleOffset;
444    GLint slices2;
445    GLint finish;
446
447    if (slices >= CACHE_SIZE) slices = CACHE_SIZE-1;
448    if (slices < 2 || loops < 1 || outerRadius <= 0.0 || innerRadius < 0.0 ||
449	    innerRadius > outerRadius) {
450	gluQuadricError(qobj, GLU_INVALID_VALUE);
451	return;
452    }
453
454    if (sweepAngle < -360.0) sweepAngle = 360.0;
455    if (sweepAngle > 360.0) sweepAngle = 360.0;
456    if (sweepAngle < 0) {
457	startAngle += sweepAngle;
458	sweepAngle = -sweepAngle;
459    }
460
461    if (sweepAngle == 360.0) {
462	slices2 = slices;
463    } else {
464	slices2 = slices + 1;
465    }
466
467    /* Compute length (needed for normal calculations) */
468    deltaRadius = outerRadius - innerRadius;
469
470    /* Cache is the vertex locations cache */
471
472    angleOffset = startAngle / 180.0 * PI;
473    for (i = 0; i <= slices; i++) {
474	angle = angleOffset + ((PI * sweepAngle) / 180.0) * i / slices;
475	sinCache[i] = SIN(angle);
476	cosCache[i] = COS(angle);
477    }
478
479    if (sweepAngle == 360.0) {
480	sinCache[slices] = sinCache[0];
481	cosCache[slices] = cosCache[0];
482    }
483
484    switch(qobj->normals) {
485      case GLU_FLAT:
486      case GLU_SMOOTH:
487	if (qobj->orientation == GLU_OUTSIDE) {
488	    glNormal3f(0.0, 0.0, 1.0);
489	} else {
490	    glNormal3f(0.0, 0.0, -1.0);
491	}
492	break;
493      default:
494      case GLU_NONE:
495	break;
496    }
497
498    switch (qobj->drawStyle) {
499      case GLU_FILL:
500	if (innerRadius == 0.0) {
501	    finish = loops - 1;
502	    /* Triangle strip for inner polygons */
503	    glBegin(GL_TRIANGLE_FAN);
504	    if (qobj->textureCoords) {
505		glTexCoord2f(0.5, 0.5);
506	    }
507	    glVertex3f(0.0, 0.0, 0.0);
508	    radiusLow = outerRadius -
509		    deltaRadius * ((float) (loops-1) / loops);
510	    if (qobj->textureCoords) {
511		texLow = radiusLow / outerRadius / 2;
512	    }
513
514	    if (qobj->orientation == GLU_OUTSIDE) {
515		for (i = slices; i >= 0; i--) {
516		    if (qobj->textureCoords) {
517			glTexCoord2f(texLow * sinCache[i] + 0.5,
518				texLow * cosCache[i] + 0.5);
519		    }
520		    glVertex3f(radiusLow * sinCache[i],
521			    radiusLow * cosCache[i], 0.0);
522		}
523	    } else {
524		for (i = 0; i <= slices; i++) {
525		    if (qobj->textureCoords) {
526			glTexCoord2f(texLow * sinCache[i] + 0.5,
527				texLow * cosCache[i] + 0.5);
528		    }
529		    glVertex3f(radiusLow * sinCache[i],
530			    radiusLow * cosCache[i], 0.0);
531		}
532	    }
533	    glEnd();
534	} else {
535	    finish = loops;
536	}
537	for (j = 0; j < finish; j++) {
538	    radiusLow = outerRadius - deltaRadius * ((float) j / loops);
539	    radiusHigh = outerRadius - deltaRadius * ((float) (j + 1) / loops);
540	    if (qobj->textureCoords) {
541		texLow = radiusLow / outerRadius / 2;
542		texHigh = radiusHigh / outerRadius / 2;
543	    }
544
545	    glBegin(GL_QUAD_STRIP);
546	    for (i = 0; i <= slices; i++) {
547		if (qobj->orientation == GLU_OUTSIDE) {
548		    if (qobj->textureCoords) {
549			glTexCoord2f(texLow * sinCache[i] + 0.5,
550				texLow * cosCache[i] + 0.5);
551		    }
552		    glVertex3f(radiusLow * sinCache[i],
553			    radiusLow * cosCache[i], 0.0);
554
555		    if (qobj->textureCoords) {
556			glTexCoord2f(texHigh * sinCache[i] + 0.5,
557				texHigh * cosCache[i] + 0.5);
558		    }
559		    glVertex3f(radiusHigh * sinCache[i],
560			    radiusHigh * cosCache[i], 0.0);
561		} else {
562		    if (qobj->textureCoords) {
563			glTexCoord2f(texHigh * sinCache[i] + 0.5,
564				texHigh * cosCache[i] + 0.5);
565		    }
566		    glVertex3f(radiusHigh * sinCache[i],
567			    radiusHigh * cosCache[i], 0.0);
568
569		    if (qobj->textureCoords) {
570			glTexCoord2f(texLow * sinCache[i] + 0.5,
571				texLow * cosCache[i] + 0.5);
572		    }
573		    glVertex3f(radiusLow * sinCache[i],
574			    radiusLow * cosCache[i], 0.0);
575		}
576	    }
577	    glEnd();
578	}
579	break;
580      case GLU_POINT:
581	glBegin(GL_POINTS);
582	for (i = 0; i < slices2; i++) {
583	    sintemp = sinCache[i];
584	    costemp = cosCache[i];
585	    for (j = 0; j <= loops; j++) {
586		radiusLow = outerRadius - deltaRadius * ((float) j / loops);
587
588		if (qobj->textureCoords) {
589		    texLow = radiusLow / outerRadius / 2;
590
591		    glTexCoord2f(texLow * sinCache[i] + 0.5,
592			    texLow * cosCache[i] + 0.5);
593		}
594		glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0);
595	    }
596	}
597	glEnd();
598	break;
599      case GLU_LINE:
600	if (innerRadius == outerRadius) {
601	    glBegin(GL_LINE_STRIP);
602
603	    for (i = 0; i <= slices; i++) {
604		if (qobj->textureCoords) {
605		    glTexCoord2f(sinCache[i] / 2 + 0.5,
606			    cosCache[i] / 2 + 0.5);
607		}
608		glVertex3f(innerRadius * sinCache[i],
609			innerRadius * cosCache[i], 0.0);
610	    }
611	    glEnd();
612	    break;
613	}
614	for (j = 0; j <= loops; j++) {
615	    radiusLow = outerRadius - deltaRadius * ((float) j / loops);
616	    if (qobj->textureCoords) {
617		texLow = radiusLow / outerRadius / 2;
618	    }
619
620	    glBegin(GL_LINE_STRIP);
621	    for (i = 0; i <= slices; i++) {
622		if (qobj->textureCoords) {
623		    glTexCoord2f(texLow * sinCache[i] + 0.5,
624			    texLow * cosCache[i] + 0.5);
625		}
626		glVertex3f(radiusLow * sinCache[i],
627			radiusLow * cosCache[i], 0.0);
628	    }
629	    glEnd();
630	}
631	for (i=0; i < slices2; i++) {
632	    sintemp = sinCache[i];
633	    costemp = cosCache[i];
634	    glBegin(GL_LINE_STRIP);
635	    for (j = 0; j <= loops; j++) {
636		radiusLow = outerRadius - deltaRadius * ((float) j / loops);
637		if (qobj->textureCoords) {
638		    texLow = radiusLow / outerRadius / 2;
639		}
640
641		if (qobj->textureCoords) {
642		    glTexCoord2f(texLow * sinCache[i] + 0.5,
643			    texLow * cosCache[i] + 0.5);
644		}
645		glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0);
646	    }
647	    glEnd();
648	}
649	break;
650      case GLU_SILHOUETTE:
651	if (sweepAngle < 360.0) {
652	    for (i = 0; i <= slices; i+= slices) {
653		sintemp = sinCache[i];
654		costemp = cosCache[i];
655		glBegin(GL_LINE_STRIP);
656		for (j = 0; j <= loops; j++) {
657		    radiusLow = outerRadius - deltaRadius * ((float) j / loops);
658
659		    if (qobj->textureCoords) {
660			texLow = radiusLow / outerRadius / 2;
661			glTexCoord2f(texLow * sinCache[i] + 0.5,
662				texLow * cosCache[i] + 0.5);
663		    }
664		    glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0);
665		}
666		glEnd();
667	    }
668	}
669	for (j = 0; j <= loops; j += loops) {
670	    radiusLow = outerRadius - deltaRadius * ((float) j / loops);
671	    if (qobj->textureCoords) {
672		texLow = radiusLow / outerRadius / 2;
673	    }
674
675	    glBegin(GL_LINE_STRIP);
676	    for (i = 0; i <= slices; i++) {
677		if (qobj->textureCoords) {
678		    glTexCoord2f(texLow * sinCache[i] + 0.5,
679			    texLow * cosCache[i] + 0.5);
680		}
681		glVertex3f(radiusLow * sinCache[i],
682			radiusLow * cosCache[i], 0.0);
683	    }
684	    glEnd();
685	    if (innerRadius == outerRadius) break;
686	}
687	break;
688      default:
689	break;
690    }
691}
692
693void GLAPIENTRY
694gluSphere(GLUquadric *qobj, GLdouble radius, GLint slices, GLint stacks)
695{
696    GLint i,j;
697    GLfloat sinCache1a[CACHE_SIZE];
698    GLfloat cosCache1a[CACHE_SIZE];
699    GLfloat sinCache2a[CACHE_SIZE];
700    GLfloat cosCache2a[CACHE_SIZE];
701    GLfloat sinCache3a[CACHE_SIZE];
702    GLfloat cosCache3a[CACHE_SIZE];
703    GLfloat sinCache1b[CACHE_SIZE];
704    GLfloat cosCache1b[CACHE_SIZE];
705    GLfloat sinCache2b[CACHE_SIZE];
706    GLfloat cosCache2b[CACHE_SIZE];
707    GLfloat sinCache3b[CACHE_SIZE];
708    GLfloat cosCache3b[CACHE_SIZE];
709    GLfloat angle;
710    GLfloat zLow, zHigh;
711    GLfloat sintemp1 = 0.0, sintemp2 = 0.0, sintemp3 = 0.0, sintemp4 = 0.0;
712    GLfloat costemp1 = 0.0, costemp2 = 0.0, costemp3 = 0.0, costemp4 = 0.0;
713    GLboolean needCache2, needCache3;
714    GLint start, finish;
715
716    if (slices >= CACHE_SIZE) slices = CACHE_SIZE-1;
717    if (stacks >= CACHE_SIZE) stacks = CACHE_SIZE-1;
718    if (slices < 2 || stacks < 1 || radius < 0.0) {
719	gluQuadricError(qobj, GLU_INVALID_VALUE);
720	return;
721    }
722
723    /* Cache is the vertex locations cache */
724    /* Cache2 is the various normals at the vertices themselves */
725    /* Cache3 is the various normals for the faces */
726    needCache2 = needCache3 = GL_FALSE;
727
728    if (qobj->normals == GLU_SMOOTH) {
729	needCache2 = GL_TRUE;
730    }
731
732    if (qobj->normals == GLU_FLAT) {
733	if (qobj->drawStyle != GLU_POINT) {
734	    needCache3 = GL_TRUE;
735	}
736	if (qobj->drawStyle == GLU_LINE) {
737	    needCache2 = GL_TRUE;
738	}
739    }
740
741    for (i = 0; i < slices; i++) {
742	angle = 2 * PI * i / slices;
743	sinCache1a[i] = SIN(angle);
744	cosCache1a[i] = COS(angle);
745	if (needCache2) {
746	    sinCache2a[i] = sinCache1a[i];
747	    cosCache2a[i] = cosCache1a[i];
748	}
749    }
750
751    for (j = 0; j <= stacks; j++) {
752	angle = PI * j / stacks;
753	if (needCache2) {
754	    if (qobj->orientation == GLU_OUTSIDE) {
755		sinCache2b[j] = SIN(angle);
756		cosCache2b[j] = COS(angle);
757	    } else {
758		sinCache2b[j] = -SIN(angle);
759		cosCache2b[j] = -COS(angle);
760	    }
761	}
762	sinCache1b[j] = radius * SIN(angle);
763	cosCache1b[j] = radius * COS(angle);
764    }
765    /* Make sure it comes to a point */
766    sinCache1b[0] = 0;
767    sinCache1b[stacks] = 0;
768
769    if (needCache3) {
770	for (i = 0; i < slices; i++) {
771	    angle = 2 * PI * (i-0.5) / slices;
772	    sinCache3a[i] = SIN(angle);
773	    cosCache3a[i] = COS(angle);
774	}
775	for (j = 0; j <= stacks; j++) {
776	    angle = PI * (j - 0.5) / stacks;
777	    if (qobj->orientation == GLU_OUTSIDE) {
778		sinCache3b[j] = SIN(angle);
779		cosCache3b[j] = COS(angle);
780	    } else {
781		sinCache3b[j] = -SIN(angle);
782		cosCache3b[j] = -COS(angle);
783	    }
784	}
785    }
786
787    sinCache1a[slices] = sinCache1a[0];
788    cosCache1a[slices] = cosCache1a[0];
789    if (needCache2) {
790	sinCache2a[slices] = sinCache2a[0];
791	cosCache2a[slices] = cosCache2a[0];
792    }
793    if (needCache3) {
794	sinCache3a[slices] = sinCache3a[0];
795	cosCache3a[slices] = cosCache3a[0];
796    }
797
798    switch (qobj->drawStyle) {
799      case GLU_FILL:
800	/* Do ends of sphere as TRIANGLE_FAN's (if not texturing)
801	** We don't do it when texturing because we need to respecify the
802	** texture coordinates of the apex for every adjacent vertex (because
803	** it isn't a constant for that point)
804	*/
805	if (!(qobj->textureCoords)) {
806	    start = 1;
807	    finish = stacks - 1;
808
809	    /* Low end first (j == 0 iteration) */
810	    sintemp2 = sinCache1b[1];
811	    zHigh = cosCache1b[1];
812	    switch(qobj->normals) {
813	      case GLU_FLAT:
814		sintemp3 = sinCache3b[1];
815		costemp3 = cosCache3b[1];
816		break;
817	      case GLU_SMOOTH:
818		sintemp3 = sinCache2b[1];
819		costemp3 = cosCache2b[1];
820		glNormal3f(sinCache2a[0] * sinCache2b[0],
821			cosCache2a[0] * sinCache2b[0],
822			cosCache2b[0]);
823		break;
824	      default:
825		break;
826	    }
827	    glBegin(GL_TRIANGLE_FAN);
828	    glVertex3f(0.0, 0.0, radius);
829	    if (qobj->orientation == GLU_OUTSIDE) {
830		for (i = slices; i >= 0; i--) {
831		    switch(qobj->normals) {
832		      case GLU_SMOOTH:
833			glNormal3f(sinCache2a[i] * sintemp3,
834				cosCache2a[i] * sintemp3,
835				costemp3);
836			break;
837		      case GLU_FLAT:
838			if (i != slices) {
839			    glNormal3f(sinCache3a[i+1] * sintemp3,
840				    cosCache3a[i+1] * sintemp3,
841				    costemp3);
842			}
843			break;
844		      case GLU_NONE:
845		      default:
846			break;
847		    }
848		    glVertex3f(sintemp2 * sinCache1a[i],
849			    sintemp2 * cosCache1a[i], zHigh);
850		}
851	    } else {
852		for (i = 0; i <= slices; i++) {
853		    switch(qobj->normals) {
854		      case GLU_SMOOTH:
855			glNormal3f(sinCache2a[i] * sintemp3,
856				cosCache2a[i] * sintemp3,
857				costemp3);
858			break;
859		      case GLU_FLAT:
860			glNormal3f(sinCache3a[i] * sintemp3,
861				cosCache3a[i] * sintemp3,
862				costemp3);
863			break;
864		      case GLU_NONE:
865		      default:
866			break;
867		    }
868		    glVertex3f(sintemp2 * sinCache1a[i],
869			    sintemp2 * cosCache1a[i], zHigh);
870		}
871	    }
872	    glEnd();
873
874	    /* High end next (j == stacks-1 iteration) */
875	    sintemp2 = sinCache1b[stacks-1];
876	    zHigh = cosCache1b[stacks-1];
877	    switch(qobj->normals) {
878	      case GLU_FLAT:
879		sintemp3 = sinCache3b[stacks];
880		costemp3 = cosCache3b[stacks];
881		break;
882	      case GLU_SMOOTH:
883		sintemp3 = sinCache2b[stacks-1];
884		costemp3 = cosCache2b[stacks-1];
885		glNormal3f(sinCache2a[stacks] * sinCache2b[stacks],
886			cosCache2a[stacks] * sinCache2b[stacks],
887			cosCache2b[stacks]);
888		break;
889	      default:
890		break;
891	    }
892	    glBegin(GL_TRIANGLE_FAN);
893	    glVertex3f(0.0, 0.0, -radius);
894	    if (qobj->orientation == GLU_OUTSIDE) {
895		for (i = 0; i <= slices; i++) {
896		    switch(qobj->normals) {
897		      case GLU_SMOOTH:
898			glNormal3f(sinCache2a[i] * sintemp3,
899				cosCache2a[i] * sintemp3,
900				costemp3);
901			break;
902		      case GLU_FLAT:
903			glNormal3f(sinCache3a[i] * sintemp3,
904				cosCache3a[i] * sintemp3,
905				costemp3);
906			break;
907		      case GLU_NONE:
908		      default:
909			break;
910		    }
911		    glVertex3f(sintemp2 * sinCache1a[i],
912			    sintemp2 * cosCache1a[i], zHigh);
913		}
914	    } else {
915		for (i = slices; i >= 0; i--) {
916		    switch(qobj->normals) {
917		      case GLU_SMOOTH:
918			glNormal3f(sinCache2a[i] * sintemp3,
919				cosCache2a[i] * sintemp3,
920				costemp3);
921			break;
922		      case GLU_FLAT:
923			if (i != slices) {
924			    glNormal3f(sinCache3a[i+1] * sintemp3,
925				    cosCache3a[i+1] * sintemp3,
926				    costemp3);
927			}
928			break;
929		      case GLU_NONE:
930		      default:
931			break;
932		    }
933		    glVertex3f(sintemp2 * sinCache1a[i],
934			    sintemp2 * cosCache1a[i], zHigh);
935		}
936	    }
937	    glEnd();
938	} else {
939	    start = 0;
940	    finish = stacks;
941	}
942	for (j = start; j < finish; j++) {
943	    zLow = cosCache1b[j];
944	    zHigh = cosCache1b[j+1];
945	    sintemp1 = sinCache1b[j];
946	    sintemp2 = sinCache1b[j+1];
947	    switch(qobj->normals) {
948	      case GLU_FLAT:
949		sintemp4 = sinCache3b[j+1];
950		costemp4 = cosCache3b[j+1];
951		break;
952	      case GLU_SMOOTH:
953		if (qobj->orientation == GLU_OUTSIDE) {
954		    sintemp3 = sinCache2b[j+1];
955		    costemp3 = cosCache2b[j+1];
956		    sintemp4 = sinCache2b[j];
957		    costemp4 = cosCache2b[j];
958		} else {
959		    sintemp3 = sinCache2b[j];
960		    costemp3 = cosCache2b[j];
961		    sintemp4 = sinCache2b[j+1];
962		    costemp4 = cosCache2b[j+1];
963		}
964		break;
965	      default:
966		break;
967	    }
968
969	    glBegin(GL_QUAD_STRIP);
970	    for (i = 0; i <= slices; i++) {
971		switch(qobj->normals) {
972		  case GLU_SMOOTH:
973		    glNormal3f(sinCache2a[i] * sintemp3,
974			    cosCache2a[i] * sintemp3,
975			    costemp3);
976		    break;
977		  case GLU_FLAT:
978		  case GLU_NONE:
979		  default:
980		    break;
981		}
982		if (qobj->orientation == GLU_OUTSIDE) {
983		    if (qobj->textureCoords) {
984			glTexCoord2f(1 - (float) i / slices,
985				1 - (float) (j+1) / stacks);
986		    }
987		    glVertex3f(sintemp2 * sinCache1a[i],
988			    sintemp2 * cosCache1a[i], zHigh);
989		} else {
990		    if (qobj->textureCoords) {
991			glTexCoord2f(1 - (float) i / slices,
992				1 - (float) j / stacks);
993		    }
994		    glVertex3f(sintemp1 * sinCache1a[i],
995			    sintemp1 * cosCache1a[i], zLow);
996		}
997		switch(qobj->normals) {
998		  case GLU_SMOOTH:
999		    glNormal3f(sinCache2a[i] * sintemp4,
1000			    cosCache2a[i] * sintemp4,
1001			    costemp4);
1002		    break;
1003		  case GLU_FLAT:
1004		    glNormal3f(sinCache3a[i] * sintemp4,
1005			    cosCache3a[i] * sintemp4,
1006			    costemp4);
1007		    break;
1008		  case GLU_NONE:
1009		  default:
1010		    break;
1011		}
1012		if (qobj->orientation == GLU_OUTSIDE) {
1013		    if (qobj->textureCoords) {
1014			glTexCoord2f(1 - (float) i / slices,
1015				1 - (float) j / stacks);
1016		    }
1017		    glVertex3f(sintemp1 * sinCache1a[i],
1018			    sintemp1 * cosCache1a[i], zLow);
1019		} else {
1020		    if (qobj->textureCoords) {
1021			glTexCoord2f(1 - (float) i / slices,
1022				1 - (float) (j+1) / stacks);
1023		    }
1024		    glVertex3f(sintemp2 * sinCache1a[i],
1025			    sintemp2 * cosCache1a[i], zHigh);
1026		}
1027	    }
1028	    glEnd();
1029	}
1030	break;
1031      case GLU_POINT:
1032	glBegin(GL_POINTS);
1033	for (j = 0; j <= stacks; j++) {
1034	    sintemp1 = sinCache1b[j];
1035	    costemp1 = cosCache1b[j];
1036	    switch(qobj->normals) {
1037	      case GLU_FLAT:
1038	      case GLU_SMOOTH:
1039		sintemp2 = sinCache2b[j];
1040		costemp2 = cosCache2b[j];
1041		break;
1042	      default:
1043		break;
1044	    }
1045	    for (i = 0; i < slices; i++) {
1046		switch(qobj->normals) {
1047		  case GLU_FLAT:
1048		  case GLU_SMOOTH:
1049		    glNormal3f(sinCache2a[i] * sintemp2,
1050			    cosCache2a[i] * sintemp2,
1051			    costemp2);
1052		    break;
1053		  case GLU_NONE:
1054		  default:
1055		    break;
1056		}
1057
1058		zLow = j * radius / stacks;
1059
1060		if (qobj->textureCoords) {
1061		    glTexCoord2f(1 - (float) i / slices,
1062			    1 - (float) j / stacks);
1063		}
1064		glVertex3f(sintemp1 * sinCache1a[i],
1065			sintemp1 * cosCache1a[i], costemp1);
1066	    }
1067	}
1068	glEnd();
1069	break;
1070      case GLU_LINE:
1071      case GLU_SILHOUETTE:
1072	for (j = 1; j < stacks; j++) {
1073	    sintemp1 = sinCache1b[j];
1074	    costemp1 = cosCache1b[j];
1075	    switch(qobj->normals) {
1076	      case GLU_FLAT:
1077	      case GLU_SMOOTH:
1078		sintemp2 = sinCache2b[j];
1079		costemp2 = cosCache2b[j];
1080		break;
1081	      default:
1082		break;
1083	    }
1084
1085	    glBegin(GL_LINE_STRIP);
1086	    for (i = 0; i <= slices; i++) {
1087		switch(qobj->normals) {
1088		  case GLU_FLAT:
1089		    glNormal3f(sinCache3a[i] * sintemp2,
1090			    cosCache3a[i] * sintemp2,
1091			    costemp2);
1092		    break;
1093		  case GLU_SMOOTH:
1094		    glNormal3f(sinCache2a[i] * sintemp2,
1095			    cosCache2a[i] * sintemp2,
1096			    costemp2);
1097		    break;
1098		  case GLU_NONE:
1099		  default:
1100		    break;
1101		}
1102		if (qobj->textureCoords) {
1103		    glTexCoord2f(1 - (float) i / slices,
1104			    1 - (float) j / stacks);
1105		}
1106		glVertex3f(sintemp1 * sinCache1a[i],
1107			sintemp1 * cosCache1a[i], costemp1);
1108	    }
1109	    glEnd();
1110	}
1111	for (i = 0; i < slices; i++) {
1112	    sintemp1 = sinCache1a[i];
1113	    costemp1 = cosCache1a[i];
1114	    switch(qobj->normals) {
1115	      case GLU_FLAT:
1116	      case GLU_SMOOTH:
1117		sintemp2 = sinCache2a[i];
1118		costemp2 = cosCache2a[i];
1119		break;
1120	      default:
1121		break;
1122	    }
1123
1124	    glBegin(GL_LINE_STRIP);
1125	    for (j = 0; j <= stacks; j++) {
1126		switch(qobj->normals) {
1127		  case GLU_FLAT:
1128		    glNormal3f(sintemp2 * sinCache3b[j],
1129			    costemp2 * sinCache3b[j],
1130			    cosCache3b[j]);
1131		    break;
1132		  case GLU_SMOOTH:
1133		    glNormal3f(sintemp2 * sinCache2b[j],
1134			    costemp2 * sinCache2b[j],
1135			    cosCache2b[j]);
1136		    break;
1137		  case GLU_NONE:
1138		  default:
1139		    break;
1140		}
1141
1142		if (qobj->textureCoords) {
1143		    glTexCoord2f(1 - (float) i / slices,
1144			    1 - (float) j / stacks);
1145		}
1146		glVertex3f(sintemp1 * sinCache1b[j],
1147			costemp1 * sinCache1b[j], cosCache1b[j]);
1148	    }
1149	    glEnd();
1150	}
1151	break;
1152      default:
1153	break;
1154    }
1155}
1156