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