1 module DrawGL;
2 
3 import gtkglc.gl;
4 import gtkglc.glu;
5 import gtkc.glibtypes;
6 
7 import std.math;
8 
9 /* 
10  * The following code is imported from GLUT.
11  *
12  * Adapted to D by Mike Wey, 2012.
13  *
14  * Copyright (c) Mark J. Kilgard, 1994, 1997.
15  *
16  * (c) Copyright 1993, Silicon Graphics, Inc.
17  * 
18  * ALL RIGHTS RESERVED
19  * 
20  * Permission to use, copy, modify, and distribute this software
21  * for any purpose and without fee is hereby granted, provided
22  * that the above copyright notice appear in all copies and that
23  * both the copyright notice and this permission notice appear in
24  * supporting documentation, and that the name of Silicon
25  * Graphics, Inc. not be used in advertising or publicity
26  * pertaining to distribution of the software without specific,
27  * written prior permission.
28  * 
29  * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU
30  * "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR
31  * OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
32  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  IN NO
33  * EVENT SHALL SILICON GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE
34  * ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR
35  * CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,
36  * INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE,
37  * SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR
38  * NOT SILICON GRAPHICS, INC.  HAS BEEN ADVISED OF THE POSSIBILITY
39  * OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
40  * ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE OR
41  * PERFORMANCE OF THIS SOFTWARE.
42  * 
43  * US Government Users Restricted Rights
44  * 
45  * Use, duplication, or disclosure by the Government is subject to
46  * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
47  * (c)(1)(ii) of the Rights in Technical Data and Computer
48  * Software clause at DFARS 252.227-7013 and/or in similar or
49  * successor clauses in the FAR or the DOD or NASA FAR
50  * Supplement.  Unpublished-- rights reserved under the copyright
51  * laws of the United States.  Contractor/manufacturer is Silicon
52  * Graphics, Inc., 2011 N.  Shoreline Blvd., Mountain View, CA
53  * 94039-7311.
54  * 
55  * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
56  */
57 
58 /* 
59  * Cube
60  */
61 
62 static void drawBox(GLfloat size, GLenum type)
63 {
64 	static GLfloat n[6][3] =
65 	[
66 		[-1.0, 0.0, 0.0],
67 		[0.0, 1.0, 0.0],
68 		[1.0, 0.0, 0.0],
69 		[0.0, -1.0, 0.0],
70 		[0.0, 0.0, 1.0],
71 		[0.0, 0.0, -1.0]
72 	];
73 	static GLint faces[6][4] =
74 	[
75 		[0, 1, 2, 3],
76 		[3, 2, 6, 7],
77 		[7, 6, 5, 4],
78 		[4, 5, 1, 0],
79 		[5, 6, 2, 1],
80 		[7, 4, 0, 3]
81 	];
82 
83 	GLfloat v[8][3];
84 	GLint i;
85 
86 	v[0][0] = v[1][0] = v[2][0] = v[3][0] = -size / 2;
87 	v[4][0] = v[5][0] = v[6][0] = v[7][0] = size / 2;
88 	v[0][1] = v[1][1] = v[4][1] = v[5][1] = -size / 2;
89 	v[2][1] = v[3][1] = v[6][1] = v[7][1] = size / 2;
90 	v[0][2] = v[3][2] = v[4][2] = v[7][2] = -size / 2;
91 	v[1][2] = v[2][2] = v[5][2] = v[6][2] = size / 2;
92 
93 	for (i = 5; i >= 0; i--) {
94 		glBegin(type);
95 		glNormal3fv(&n[i][0]);
96 		glVertex3fv(&v[faces[i][0]][0]);
97 		glVertex3fv(&v[faces[i][1]][0]);
98 		glVertex3fv(&v[faces[i][2]][0]);
99 		glVertex3fv(&v[faces[i][3]][0]);
100 		glEnd();
101 	}
102 }
103 
104 /**
105  * Renders a cube.
106  * The cube is centered at the modeling coordinates origin with sides of
107  * length size.
108  * 
109  * Params:
110  *     solid = true if the cube should be solid.
111  *     size  =  length of cube sides.
112  **/
113 void drawCube(bool solid, double size)
114 {
115 	if (solid)
116 		drawBox (size, GL_QUADS);
117 	else
118 		drawBox (size, GL_LINE_LOOP);
119 }
120 
121 /*
122  * Quadrics
123  */
124 
125 static GLUquadric* quadObj = null;
126 
127 static void initQuadObj()
128 {
129 	if ( quadObj !is null )
130 		return;
131 
132 	quadObj = gluNewQuadric();
133 
134 	if (!quadObj)
135 		throw new Error("out of memory.");
136 }
137 
138 /**
139  * Renders a sphere centered at the modeling coordinates origin of
140  * the specified @radius. The sphere is subdivided around the Z axis into
141  * slices and along the Z axis into stacks.
142  * 
143  * Params:
144  *     solid  = true if the sphere should be solid.
145  *     radius = the radius of the sphere.
146  *     slices = the number of subdivisions around the Z axis
147  *              (similar to lines of longitude).
148  *     stacks = the number of subdivisions along the Z axis
149  *              (similar to lines of latitude).
150  **/
151 void drawSphere(bool solid, double radius, int slices, int stacks)
152 {
153 	initQuadObj();
154 
155 	if (solid)
156 		gluQuadricDrawStyle (quadObj, GLU_FILL);
157 	else
158 		gluQuadricDrawStyle (quadObj, GLU_LINE);
159 
160 	gluQuadricNormals (quadObj, GLU_SMOOTH);
161 
162 	/* If we ever changed/used the texture or orientation state
163 	   of quadObj, we'd need to change it to the defaults here
164 	   with gluQuadricTexture and/or gluQuadricOrientation. */
165 	gluSphere (quadObj, radius, slices, stacks);
166 }
167 
168 /**
169  * Renders a cone oriented along the Z axis.
170  * The base of the cone is placed at Z = 0, and the top at Z = height.
171  * The cone is subdivided around the Z axis into slices, and along
172  * the Z axis into stacks. 
173  *
174  * Params:
175  *     solid  = true if the cone should be solid.
176  *     base   = the radius of the base of the cone.
177  *     height = the height of the cone.
178  *     slices = the number of subdivisions around the Z axis.
179  *     stacks = the number of subdivisions along the Z axis.
180  **/
181 void drawCone(bool solid, double base, double height, int slices, int stacks)
182 {
183 	initQuadObj();
184 
185 	if (solid)
186 		gluQuadricDrawStyle (quadObj, GLU_FILL);
187 	else
188 		gluQuadricDrawStyle (quadObj, GLU_LINE);
189 
190 	gluQuadricNormals (quadObj, GLU_SMOOTH);
191 
192 	/* If we ever changed/used the texture or orientation state
193 	   of quadObj, we'd need to change it to the defaults here
194 	   with gluQuadricTexture and/or gluQuadricOrientation. */
195 	gluCylinder (quadObj, base, 0.0, height, slices, stacks);
196 }
197 
198 /* 
199  * Torus
200  */
201 
202 static void doughnut(GLfloat r, GLfloat R, GLint nsides, GLint rings)
203 {
204 	int i, j;
205 	GLfloat theta, phi, theta1;
206 	GLfloat cosTheta, sinTheta;
207 	GLfloat cosTheta1, sinTheta1;
208 	GLfloat ringDelta, sideDelta;
209 	ringDelta = 2.0 * PI / rings;
210 	sideDelta = 2.0 * PI / nsides;
211 
212 	theta = 0.0;
213 	cosTheta = 1.0;
214 	sinTheta = 0.0;
215 	for (i = rings - 1; i >= 0; i--) {
216 		theta1 = theta + ringDelta;
217 		cosTheta1 = cos(theta1);
218 		sinTheta1 = sin(theta1);
219 		glBegin(GL_QUAD_STRIP);
220 		phi = 0.0;
221 		for (j = nsides; j >= 0; j--) {
222 			GLfloat cosPhi, sinPhi, dist;
223 
224 			phi += sideDelta;
225 			cosPhi = cos(phi);
226 			sinPhi = sin(phi);
227 			dist = R + r * cosPhi;
228 
229 			glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
230 			glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
231 			glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
232 			glVertex3f(cosTheta * dist, -sinTheta * dist,  r * sinPhi);
233 		}
234 		glEnd();
235 		theta = theta1;
236 		cosTheta = cosTheta1;
237 		sinTheta = sinTheta1;
238 	}
239 }
240 
241 /**
242  * Renders a torus (doughnut) centered at the modeling coordinates
243  * origin whose axis is aligned with the Z axis.
244  *
245  * Params:
246  *     solid        = true if the torus should be solid.
247  *     inner_radius = inner radius of the torus.
248  *     outer_radius = outer radius of the torus.
249  *     nsides       = number of sides for each radial section.
250  *     rings        = number of radial divisions for the torus.
251  **/
252 void drawTorus (bool solid, double inner_radius, double outer_radius, int nsides, int rings)
253 {
254 	if (solid)
255 	{
256 		doughnut (inner_radius, outer_radius, nsides, rings);
257 	}
258 	else
259 	{
260 		glPushAttrib (GL_POLYGON_BIT);
261 		glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
262 		doughnut (inner_radius, outer_radius, nsides, rings);
263 		glPopAttrib ();
264 	}
265 }
266 
267 static void diff3(T, U, V)(T a, U b, ref V c)
268 {
269 	c[0] = a[0] - b[0];
270 	c[1] = a[1] - b[1];
271 	c[2] = a[2] - b[2];
272 }
273 
274 
275 static void crossprod(GLfloat[3] v1, GLfloat[3] v2, ref GLfloat[3] prod)
276 {
277   GLfloat p[3];         /* in case prod == v1 or v2 */
278 
279   p[0] = v1[1] * v2[2] - v2[1] * v1[2];
280   p[1] = v1[2] * v2[0] - v2[2] * v1[0];
281   p[2] = v1[0] * v2[1] - v2[0] * v1[1];
282   prod[0] = p[0];
283   prod[1] = p[1];
284   prod[2] = p[2];
285 }
286 
287 static void normalize(ref GLfloat[3] v)
288 {
289 	GLfloat d;
290 
291 	d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
292 	if (d == 0.0) {
293 		//g_warning("normalize: zero length vector");
294 		v[0] = d = 1.0;
295 	}
296 	d = 1 / d;
297 	v[0] *= d;
298 	v[1] *= d;
299 	v[2] *= d;
300 }
301 
302 static void recorditem(GLfloat[3] n1, GLfloat[3] n2, GLfloat[3] n3, GLenum shadeType)
303 {
304 	GLfloat[3] q0, q1;
305 
306 	diff3(n1, n2, q0);
307 	diff3(n2, n3, q1);
308 	crossprod(q0, q1, q1);
309 	normalize(q1);
310 
311 	glBegin(shadeType);
312 	glNormal3fv(q1.ptr);
313 	glVertex3fv(n1.ptr);
314 	glVertex3fv(n2.ptr);
315 	glVertex3fv(n3.ptr);
316 	glEnd();
317 }
318 
319 static void subdivide(GLfloat[3] v0, GLfloat[3] v1, GLfloat[3] v2, GLenum shadeType)
320 {
321 	int depth;
322 	GLfloat[3] w0, w1, w2;
323 	GLfloat l;
324 	int i, j, k, n;
325 
326 	depth = 1;
327 	for (i = 0; i < depth; i++) {
328 		for (j = 0; i + j < depth; j++) {
329 			k = depth - i - j;
330 			for (n = 0; n < 3; n++) {
331 				w0[n] = (i * v0[n] + j * v1[n] + k * v2[n]) / depth;
332 				w1[n] = ((i + 1) * v0[n] + j * v1[n] + (k - 1) * v2[n])
333 				  / depth;
334 				w2[n] = (i * v0[n] + (j + 1) * v1[n] + (k - 1) * v2[n])
335 				  / depth;
336 			}
337 			l = sqrt(w0[0] * w0[0] + w0[1] * w0[1] + w0[2] * w0[2]);
338 			w0[0] /= l;
339 			w0[1] /= l;
340 			w0[2] /= l;
341 			l = sqrt(w1[0] * w1[0] + w1[1] * w1[1] + w1[2] * w1[2]);
342 			w1[0] /= l;
343 			w1[1] /= l;
344 			w1[2] /= l;
345 			l = sqrt(w2[0] * w2[0] + w2[1] * w2[1] + w2[2] * w2[2]);
346 			w2[0] /= l;
347 			w2[1] /= l;
348 			w2[2] /= l;
349 			recorditem(w1, w0, w2, shadeType);
350 		}
351 	}
352 }
353 
354 static void drawtriangle(int i, GLfloat[3][] data, int[3][] ndx, GLenum shadeType)
355 {
356 	GLfloat[3] x0, x1, x2;
357 
358 	x0 = data[ndx[i][0]];
359 	x1 = data[ndx[i][1]];
360 	x2 = data[ndx[i][2]];
361 	subdivide(x0, x1, x2, shadeType);
362 }
363 
364 /* 
365  * Tetrahedron
366  */
367 
368 /* tetrahedron data: */
369 
370 const T = 1.73205080756887729;
371 
372 static GLfloat tdata[4][3] =
373 [
374 	[T, T, T],
375 	[T, -T, -T],
376 	[-T, T, -T],
377 	[-T, -T, T]
378 ];
379 
380 static int tndex[4][3] =
381 [
382 	[0, 1, 3],
383 	[2, 1, 0],
384 	[3, 2, 0],
385 	[1, 2, 3]
386 ];
387 
388 static void tetrahedron(GLenum shadeType)
389 {
390 	int i;
391 
392 	for (i = 3; i >= 0; i--)
393 		drawtriangle(i, tdata, tndex, shadeType);
394 }
395 
396 /**
397  * Renders a tetrahedron centered at the modeling coordinates
398  * origin with a radius of the square root of 3.
399  *
400  * Params:
401  *     solid = true if the tetrahedron should be solid.
402  **/
403 void drawTetrahedron (bool solid)
404 {
405 	if (solid)
406 		tetrahedron (GL_TRIANGLES);
407 	else
408 		tetrahedron (GL_LINE_LOOP);
409 }
410 
411 /* 
412  * Octahedron
413  */
414 
415 /* octahedron data: The octahedron produced is centered at the
416    origin and has radius 1.0 */
417 static GLfloat odata[6][3] =
418 [
419 	[1.0, 0.0, 0.0],
420 	[-1.0, 0.0, 0.0],
421 	[0.0, 1.0, 0.0],
422 	[0.0, -1.0, 0.0],
423 	[0.0, 0.0, 1.0],
424 	[0.0, 0.0, -1.0]
425 ];
426 
427 static int ondex[8][3] =
428 [
429 	[0, 4, 2],
430 	[1, 2, 4],
431 	[0, 3, 4],
432 	[1, 4, 3],
433 	[0, 2, 5],
434 	[1, 5, 2],
435 	[0, 5, 3],
436 	[1, 3, 5]
437 ];
438 
439 static void octahedron(GLenum shadeType)
440 {
441 	int i;
442 
443 	for (i = 7; i >= 0; i--) {
444 		drawtriangle(i, odata, ondex, shadeType);
445 	}
446 }
447 
448 /**
449  * Renders a octahedron centered at the modeling coordinates
450  * origin with a radius of 1.0.
451  *
452  * Params:
453  *     solid = true if the octahedron should be solid.
454  **/
455 void drawOctahedron (bool solid)
456 {
457 	if (solid)
458 		octahedron (GL_TRIANGLES);
459 	else
460 		octahedron (GL_LINE_LOOP);
461 }
462 
463 /* 
464  * Icosahedron
465  */
466 
467 /* icosahedron data: These numbers are rigged to make an
468    icosahedron of radius 1.0 */
469 
470 const X = 0.525731112119133606;
471 const Z = 0.850650808352039932;
472 
473 static GLfloat idata[12][3] =
474 [
475 	[-X, 0, Z],
476 	[X, 0, Z],
477 	[-X, 0, -Z],
478 	[X, 0, -Z],
479 	[0, Z, X],
480 	[0, Z, -X],
481 	[0, -Z, X],
482 	[0, -Z, -X],
483 	[Z, X, 0],
484 	[-Z, X, 0],
485 	[Z, -X, 0],
486 	[-Z, -X, 0]
487 ];
488 
489 static int index[20][3] =
490 [
491 	[0, 4, 1],
492 	[0, 9, 4],
493 	[9, 5, 4],
494 	[4, 5, 8],
495 	[4, 8, 1],
496 	[8, 10, 1],
497 	[8, 3, 10],
498 	[5, 3, 8],
499 	[5, 2, 3],
500 	[2, 7, 3],
501 	[7, 10, 3],
502 	[7, 6, 10],
503 	[7, 11, 6],
504 	[11, 0, 6],
505 	[0, 1, 6],
506 	[6, 1, 10],
507 	[9, 0, 11],
508 	[9, 11, 2],
509 	[9, 2, 5],
510 	[7, 2, 11],
511 ];
512 
513 static void icosahedron(GLenum shadeType)
514 {
515 	int i;
516 
517 	for (i = 19; i >= 0; i--) {
518 		drawtriangle(i, idata, index, shadeType);
519 	}
520 }
521 
522 /**
523  * Renders a icosahedron.
524  * The icosahedron is centered at the modeling coordinates origin
525  * and has a radius of 1.0. 
526  *
527  * Params:
528  *     solid = true if the icosahedron should be solid.
529  **/
530 void drawIcosahedron (bool solid)
531 {
532 	if (solid)
533 		icosahedron (GL_TRIANGLES);
534 	else
535 		icosahedron (GL_LINE_LOOP);
536 }
537 
538 /* 
539  * Dodecahedron
540  */
541 
542 static GLfloat dodec[20][3];
543 
544 static void initDodecahedron()
545 {
546 	GLfloat alpha, beta;
547 
548 	alpha = sqrt(2.0 / (3.0 + sqrt(5.0)));
549 	beta = 1.0 + sqrt(6.0 / (3.0 + sqrt(5.0)) -
550 		2.0 + 2.0 * sqrt(2.0 / (3.0 + sqrt(5.0))));
551 	/* *INDENT-OFF* */
552 	dodec[0][0] = -alpha; dodec[0][1] = 0; dodec[0][2] = beta;
553 	dodec[1][0] = alpha; dodec[1][1] = 0; dodec[1][2] = beta;
554 	dodec[2][0] = -1; dodec[2][1] = -1; dodec[2][2] = -1;
555 	dodec[3][0] = -1; dodec[3][1] = -1; dodec[3][2] = 1;
556 	dodec[4][0] = -1; dodec[4][1] = 1; dodec[4][2] = -1;
557 	dodec[5][0] = -1; dodec[5][1] = 1; dodec[5][2] = 1;
558 	dodec[6][0] = 1; dodec[6][1] = -1; dodec[6][2] = -1;
559 	dodec[7][0] = 1; dodec[7][1] = -1; dodec[7][2] = 1;
560 	dodec[8][0] = 1; dodec[8][1] = 1; dodec[8][2] = -1;
561 	dodec[9][0] = 1; dodec[9][1] = 1; dodec[9][2] = 1;
562 	dodec[10][0] = beta; dodec[10][1] = alpha; dodec[10][2] = 0;
563 	dodec[11][0] = beta; dodec[11][1] = -alpha; dodec[11][2] = 0;
564 	dodec[12][0] = -beta; dodec[12][1] = alpha; dodec[12][2] = 0;
565 	dodec[13][0] = -beta; dodec[13][1] = -alpha; dodec[13][2] = 0;
566 	dodec[14][0] = -alpha; dodec[14][1] = 0; dodec[14][2] = -beta;
567 	dodec[15][0] = alpha; dodec[15][1] = 0; dodec[15][2] = -beta;
568 	dodec[16][0] = 0; dodec[16][1] = beta; dodec[16][2] = alpha;
569 	dodec[17][0] = 0; dodec[17][1] = beta; dodec[17][2] = -alpha;
570 	dodec[18][0] = 0; dodec[18][1] = -beta; dodec[18][2] = alpha;
571 	dodec[19][0] = 0; dodec[19][1] = -beta; dodec[19][2] = -alpha;
572 	/* *INDENT-ON* */
573 
574 }
575 
576 static void pentagon(int a, int b, int c, int d, int e, GLenum shadeType)
577 {
578 	GLfloat[3] n0, d1, d2;
579 
580 	diff3(dodec[a], dodec[b], d1);
581 	diff3(dodec[b], dodec[c], d2);
582 	crossprod(d1, d2, n0);
583 	normalize(n0);
584 
585 	glBegin(shadeType);
586 	glNormal3fv(n0.ptr);
587 	glVertex3fv(&dodec[a][0]);
588 	glVertex3fv(&dodec[b][0]);
589 	glVertex3fv(&dodec[c][0]);
590 	glVertex3fv(&dodec[d][0]);
591 	glVertex3fv(&dodec[e][0]);
592 	glEnd();
593 }
594 
595 static void dodecahedron(GLenum type)
596 {
597 	static int inited = 0;
598 
599 	if (inited == 0) {
600 		inited = 1;
601 		initDodecahedron();
602 	}
603 	pentagon(0, 1, 9, 16, 5, type);
604 	pentagon(1, 0, 3, 18, 7, type);
605 	pentagon(1, 7, 11, 10, 9, type);
606 	pentagon(11, 7, 18, 19, 6, type);
607 	pentagon(8, 17, 16, 9, 10, type);
608 	pentagon(2, 14, 15, 6, 19, type);
609 	pentagon(2, 13, 12, 4, 14, type);
610 	pentagon(2, 19, 18, 3, 13, type);
611 	pentagon(3, 0, 5, 12, 13, type);
612 	pentagon(6, 15, 8, 10, 11, type);
613 	pentagon(4, 17, 8, 15, 14, type);
614 	pentagon(4, 12, 5, 16, 17, type);
615 }
616 
617 /**
618  * Renders a dodecahedron centered at the modeling coordinates
619  * origin with a radius of the square root of 3.
620  *
621  * Params:
622  *     solid = true if the dodecahedron should be solid.
623  **/
624 void drawDodecahedron (bool solid)
625 {
626 	if (solid)
627 		dodecahedron (GL_TRIANGLE_FAN);
628 	else
629 		dodecahedron (GL_LINE_LOOP);
630 }
631 
632 /* 
633  * Teapot
634  */
635 
636 /* Rim, body, lid, and bottom data must be reflected in x and
637    y; handle and spout data across the y axis only.  */
638 
639 static int patchdata[][16] =
640 [
641 		/* rim */
642 	[102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11,
643 		12, 13, 14, 15],
644 		/* body */
645 	[12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
646 		24, 25, 26, 27],
647 	[24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36,
648 		37, 38, 39, 40],
649 		/* lid */
650 	[96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101,
651 		101, 0, 1, 2, 3,],
652 	[0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112,
653 		113, 114, 115, 116, 117],
654 		/* bottom */
655 	[118, 118, 118, 118, 124, 122, 119, 121, 123, 126,
656 		125, 120, 40, 39, 38, 37],
657 		/* handle */
658 	[41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
659 		53, 54, 55, 56],
660 	[53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
661 		28, 65, 66, 67],
662 		/* spout */
663 	[68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
664 		80, 81, 82, 83],
665 	[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
666 		92, 93, 94, 95]
667 ];
668 /* *INDENT-OFF* */
669 
670 static float cpdata[][3] =
671 [
672 	[0.2, 0, 2.7], [0.2, -0.112, 2.7], [0.112, -0.2, 2.7], [0,
673 	-0.2, 2.7], [1.3375, 0, 2.53125], [1.3375, -0.749, 2.53125],
674 	[0.749, -1.3375, 2.53125], [0, -1.3375, 2.53125], [1.4375,
675 	0, 2.53125], [1.4375, -0.805, 2.53125], [0.805, -1.4375,
676 	2.53125], [0, -1.4375, 2.53125], [1.5, 0, 2.4], [1.5, -0.84,
677 	2.4], [0.84, -1.5, 2.4], [0, -1.5, 2.4], [1.75, 0, 1.875],
678 	[1.75, -0.98, 1.875], [0.98, -1.75, 1.875], [0, -1.75,
679 	1.875], [2, 0, 1.35], [2, -1.12, 1.35], [1.12, -2, 1.35],
680 	[0, -2, 1.35], [2, 0, 0.9], [2, -1.12, 0.9], [1.12, -2,
681 	0.9], [0, -2, 0.9], [-2, 0, 0.9], [2, 0, 0.45], [2, -1.12,
682 	0.45], [1.12, -2, 0.45], [0, -2, 0.45], [1.5, 0, 0.225],
683 	[1.5, -0.84, 0.225], [0.84, -1.5, 0.225], [0, -1.5, 0.225],
684 	[1.5, 0, 0.15], [1.5, -0.84, 0.15], [0.84, -1.5, 0.15], [0,
685 	-1.5, 0.15], [-1.6, 0, 2.025], [-1.6, -0.3, 2.025], [-1.5,
686 	-0.3, 2.25], [-1.5, 0, 2.25], [-2.3, 0, 2.025], [-2.3, -0.3,
687 	2.025], [-2.5, -0.3, 2.25], [-2.5, 0, 2.25], [-2.7, 0,
688 	2.025], [-2.7, -0.3, 2.025], [-3, -0.3, 2.25], [-3, 0,
689 	2.25], [-2.7, 0, 1.8], [-2.7, -0.3, 1.8], [-3, -0.3, 1.8],
690 	[-3, 0, 1.8], [-2.7, 0, 1.575], [-2.7, -0.3, 1.575], [-3,
691 	-0.3, 1.35], [-3, 0, 1.35], [-2.5, 0, 1.125], [-2.5, -0.3,
692 	1.125], [-2.65, -0.3, 0.9375], [-2.65, 0, 0.9375], [-2,
693 	-0.3, 0.9], [-1.9, -0.3, 0.6], [-1.9, 0, 0.6], [1.7, 0,
694 	1.425], [1.7, -0.66, 1.425], [1.7, -0.66, 0.6], [1.7, 0,
695 	0.6], [2.6, 0, 1.425], [2.6, -0.66, 1.425], [3.1, -0.66,
696 	0.825], [3.1, 0, 0.825], [2.3, 0, 2.1], [2.3, -0.25, 2.1],
697 	[2.4, -0.25, 2.025], [2.4, 0, 2.025], [2.7, 0, 2.4], [2.7,
698 	-0.25, 2.4], [3.3, -0.25, 2.4], [3.3, 0, 2.4], [2.8, 0,
699 	2.475], [2.8, -0.25, 2.475], [3.525, -0.25, 2.49375],
700 	[3.525, 0, 2.49375], [2.9, 0, 2.475], [2.9, -0.15, 2.475],
701 	[3.45, -0.15, 2.5125], [3.45, 0, 2.5125], [2.8, 0, 2.4],
702 	[2.8, -0.15, 2.4], [3.2, -0.15, 2.4], [3.2, 0, 2.4], [0, 0,
703 	3.15], [0.8, 0, 3.15], [0.8, -0.45, 3.15], [0.45, -0.8,
704 	3.15], [0, -0.8, 3.15], [0, 0, 2.85], [1.4, 0, 2.4], [1.4,
705 	-0.784, 2.4], [0.784, -1.4, 2.4], [0, -1.4, 2.4], [0.4, 0,
706 	2.55], [0.4, -0.224, 2.55], [0.224, -0.4, 2.55], [0, -0.4,
707 	2.55], [1.3, 0, 2.55], [1.3, -0.728, 2.55], [0.728, -1.3,
708 	2.55], [0, -1.3, 2.55], [1.3, 0, 2.4], [1.3, -0.728, 2.4],
709 	[0.728, -1.3, 2.4], [0, -1.3, 2.4], [0, 0, 0], [1.425,
710 	-0.798, 0], [1.5, 0, 0.075], [1.425, 0, 0], [0.798, -1.425,
711 	0], [0, -1.5, 0.075], [0, -1.425, 0], [1.5, -0.84, 0.075],
712 	[0.84, -1.5, 0.075]
713 ];
714 
715 static float tex[2][2][2] =
716 [
717 	[ [0, 0],
718 		[1, 0]],
719 	[ [0, 1],
720 		[1, 1]]
721 ];
722 
723 /* *INDENT-ON* */
724 
725 static void teapot(GLint grid, GLdouble scale, GLenum type)
726 {
727 	float[3][4][4] p, q, r, s;
728 	long i, j, k, l;
729 
730 	glPushAttrib(GL_ENABLE_BIT | GL_EVAL_BIT);
731 	glEnable(GL_AUTO_NORMAL);
732 	glEnable(GL_NORMALIZE);
733 	glEnable(GL_MAP2_VERTEX_3);
734 	glEnable(GL_MAP2_TEXTURE_COORD_2);
735 	glPushMatrix();
736 	glRotatef(270.0, 1.0, 0.0, 0.0);
737 	glScalef(0.5 * scale, 0.5 * scale, 0.5 * scale);
738 	glTranslatef(0.0, 0.0, -1.5);
739 	for (i = 0; i < 10; i++) {
740 		for (j = 0; j < 4; j++) {
741 			for (k = 0; k < 4; k++) {
742 				for (l = 0; l < 3; l++) {
743 					p[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
744 					q[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l];
745 					if (l == 1)
746 						q[j][k][l] *= -1.0;
747 					if (i < 6) {
748 						r[j][k][l] =
749 							cpdata[patchdata[i][j * 4 + (3 - k)]][l];
750 						if (l == 0)
751 							r[j][k][l] *= -1.0;
752 						s[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
753 						if (l == 0)
754 							s[j][k][l] *= -1.0;
755 						if (l == 1)
756 							s[j][k][l] *= -1.0;
757 					}
758 				}
759 			}
760 		}
761 		glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, &tex[0][0][0]);
762 		glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &p[0][0][0]);
763 		glMapGrid2f(grid, 0.0, 1.0, grid, 0.0, 1.0);
764 		glEvalMesh2(type, 0, grid, 0, grid);
765 		glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &q[0][0][0]);
766 		glEvalMesh2(type, 0, grid, 0, grid);
767 		if (i < 6) {
768 			glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &r[0][0][0]);
769 			glEvalMesh2(type, 0, grid, 0, grid);
770 			glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &s[0][0][0]);
771 			glEvalMesh2(type, 0, grid, 0, grid);
772 		}
773 	}
774 	glPopMatrix();
775 	glPopAttrib();
776 }
777 
778 /**
779  * Renders a teapot.
780  * Both surface normals and texture coordinates for the teapot are generated.
781  * The teapot is generated with OpenGL evaluators. 
782  *
783  * Params:
784  *     solid = true if the teapot should be solid.
785  *     scale = relative size of the teapot.
786  **/
787 void drawTeapot (bool solid, double scale)
788 {
789 	if (solid)
790 		teapot (7, scale, GL_FILL);
791 	else
792 		teapot (10, scale, GL_LINE);
793 }