initial start at rewrite of fg_geometry functions, using glDrawArrays.
[freeglut] / src / fg_geometry.c
1 /*
2  * freeglut_geometry.c
3  *
4  * Freeglut geometry rendering methods.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Creation date: Fri Dec 3 1999
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #include <GL/freeglut.h>
29 #include "fg_internal.h"
30
31 /*
32  * TODO BEFORE THE STABLE RELEASE:
33  *
34  * See fghTetrahedron
35  *
36  * Following functions have been contributed by Andreas Umbach.
37  *
38  *      glutWireCube()          -- looks OK
39  *      glutSolidCube()         -- OK
40  *
41  * Those functions have been implemented by John Fay.
42  *
43  *      glutWireTorus()         -- looks OK
44  *      glutSolidTorus()        -- looks OK
45  *      glutWireDodecahedron()  -- looks OK
46  *      glutSolidDodecahedron() -- looks OK
47  *      glutWireOctahedron()    -- looks OK
48  *      glutSolidOctahedron()   -- looks OK
49  *      glutWireTetrahedron()   -- looks OK
50  *      glutSolidTetrahedron()  -- looks OK
51  *      glutWireIcosahedron()   -- looks OK
52  *      glutSolidIcosahedron()  -- looks OK
53  *
54  *  The Following functions have been updated by Nigel Stewart, based
55  *  on FreeGLUT 2.0.0 implementations:
56  *
57  *      glutWireSphere()        -- looks OK
58  *      glutSolidSphere()       -- looks OK
59  *      glutWireCone()          -- looks OK
60  *      glutSolidCone()         -- looks OK
61  */
62
63
64 /*
65  * General function for drawing geometry. As for all geometry we have no 
66  * redundancy (or hardly any in the case of cones and cylinders) in terms
67  * of the vertex/normal combinations, we just use glDrawArrays.
68  * useWireMode controls the drawing of solids (false) or wire frame
69  * versions (TRUE) of the geometry you pass
70  */
71 static void fghDrawGeometry(GLenum vertexMode, double* vertices, double* normals, GLsizei numVertices, GLboolean useWireMode)
72 {
73     if (useWireMode)
74     {
75         glPushAttrib(GL_POLYGON_BIT);
76         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
77     }
78
79     glEnableClientState(GL_VERTEX_ARRAY);
80     glEnableClientState(GL_NORMAL_ARRAY);
81
82     glVertexPointer(3, GL_DOUBLE, 0, vertices);
83     glNormalPointer(GL_DOUBLE, 0, normals);
84     glDrawArrays(vertexMode,0,numVertices);
85
86     glDisableClientState(GL_VERTEX_ARRAY);
87     glDisableClientState(GL_NORMAL_ARRAY);
88
89     if (useWireMode)
90     {
91         glPopAttrib();
92     }
93 }
94
95
96 /* -- INTERNAL SETUP OF GEOMETRY --------------------------------------- */
97 /* -- first the cachable ones -- */
98
99 /* Magic Numbers:  r0 = ( 1, 0, 0 )
100  *                 r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
101  *                 r2 = ( -1/3, - sqrt(2) / 3,  sqrt(6) / 3 )
102  *                 r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
103  * |r0| = |r1| = |r2| = |r3| = 1
104  * Distance between any two points is 2 sqrt(6) / 3
105  *
106  * Normals:  The unit normals are simply the negative of the coordinates of the point not on the surface.
107 */
108
109 /* -- TetraHedron -- */
110 #define TETR_NUM_FACES          4
111 #define TETR_NUM_VERT_PER_FACE  3
112
113 /* Vertex Coordinates */
114 static GLdouble tet_r[TETR_NUM_FACES][TETR_NUM_VERT_PER_FACE] =
115 {
116     {             1.0,             0.0,             0.0 },
117     { -0.333333333333,  0.942809041582,             0.0 },
118     { -0.333333333333, -0.471404520791,  0.816496580928 },
119     { -0.333333333333, -0.471404520791, -0.816496580928 }
120 };
121
122 /* Vertex indices */
123 static GLubyte tet_i[TETR_NUM_FACES][TETR_NUM_VERT_PER_FACE] =
124 {
125     { 1, 3, 2 }, { 0, 2, 3 }, { 0, 3, 1 }, { 0, 1, 2 }
126 };
127 /* Normal indices */
128 static GLubyte tet_n[TETR_NUM_FACES] =
129 {
130     0, 1, 2, 3
131 };
132
133 /* Cache of input to glDrawArrays */
134 static GLboolean tetrCached = FALSE;
135 static double tetr_verts[TETR_NUM_FACES * TETR_NUM_VERT_PER_FACE * 3];
136 static double tetr_norms[TETR_NUM_FACES * TETR_NUM_VERT_PER_FACE * 3];
137
138 static void fghTetrahedronCache()
139 {
140     int p,q;
141     /*
142     * Build array with vertices from vertex coordinates and vertex indices 
143     * Do same for normals.
144     * Need to do this because of different normals at shared vertices
145     * (and because normals' coordinates need to be negated).
146     */
147     for (p=0; p<TETR_NUM_FACES; p++)
148     {
149         for (q=0; q<TETR_NUM_VERT_PER_FACE; q++)
150         {
151             int idx = p*TETR_NUM_VERT_PER_FACE*3+q*3;
152             tetr_verts[idx  ] =  tet_r[tet_i[p][q]][0];
153             tetr_verts[idx+1] =  tet_r[tet_i[p][q]][1];
154             tetr_verts[idx+2] =  tet_r[tet_i[p][q]][2];
155
156             tetr_norms[idx  ] = -tet_r[tet_n[p]][0];
157             tetr_norms[idx+1] = -tet_r[tet_n[p]][1];
158             tetr_norms[idx+2] = -tet_r[tet_n[p]][2];
159         }
160     }
161 }
162
163 /* -- Now the various shapes involving circles -- */
164 /*
165  * Compute lookup table of cos and sin values forming a cirle
166  *
167  * Notes:
168  *    It is the responsibility of the caller to free these tables
169  *    The size of the table is (n+1) to form a connected loop
170  *    The last entry is exactly the same as the first
171  *    The sign of n can be flipped to get the reverse loop
172  */
173 static void fghCircleTable(double **sint,double **cost,const int n)
174 {
175     int i;
176
177     /* Table size, the sign of n flips the circle direction */
178
179     const int size = abs(n);
180
181     /* Determine the angle between samples */
182
183     const double angle = 2*M_PI/(double)( ( n == 0 ) ? 1 : n );
184
185     /* Allocate memory for n samples, plus duplicate of first entry at the end */
186
187     *sint = (double *) calloc(sizeof(double), size+1);
188     *cost = (double *) calloc(sizeof(double), size+1);
189
190     /* Bail out if memory allocation fails, fgError never returns */
191
192     if (!(*sint) || !(*cost))
193     {
194         free(*sint);
195         free(*cost);
196         fgError("Failed to allocate memory in fghCircleTable");
197     }
198
199     /* Compute cos and sin around the circle */
200
201     (*sint)[0] = 0.0;
202     (*cost)[0] = 1.0;
203
204     for (i=1; i<size; i++)
205     {
206         (*sint)[i] = sin(angle*i);
207         (*cost)[i] = cos(angle*i);
208     }
209
210     /* Last sample is duplicate of the first */
211
212     (*sint)[size] = (*sint)[0];
213     (*cost)[size] = (*cost)[0];
214 }
215
216
217 /* -- INTERNAL DRAWING functions to avoid code duplication ------------- */
218
219 static void fghTetrahedron( GLboolean useWireMode )
220 {
221     if (!tetrCached)
222         fghTetrahedronCache();
223
224     fghDrawGeometry(GL_TRIANGLES,tetr_verts,tetr_norms,TETR_NUM_FACES*TETR_NUM_VERT_PER_FACE,useWireMode);
225 }
226
227
228 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
229
230 /*
231  * Draws a wireframed cube. Code contributed by Andreas Umbach <marvin@dataway.ch>
232  */
233 void FGAPIENTRY glutWireCube( GLdouble dSize )
234 {
235     double size = dSize * 0.5;
236
237     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
238
239 #   define V(a,b,c) glVertex3d( a size, b size, c size );
240 #   define N(a,b,c) glNormal3d( a, b, c );
241
242     /* PWO: I dared to convert the code to use macros... */
243     glBegin( GL_LINE_LOOP ); N( 1.0, 0.0, 0.0); V(+,-,+); V(+,-,-); V(+,+,-); V(+,+,+); glEnd();
244     glBegin( GL_LINE_LOOP ); N( 0.0, 1.0, 0.0); V(+,+,+); V(+,+,-); V(-,+,-); V(-,+,+); glEnd();
245     glBegin( GL_LINE_LOOP ); N( 0.0, 0.0, 1.0); V(+,+,+); V(-,+,+); V(-,-,+); V(+,-,+); glEnd();
246     glBegin( GL_LINE_LOOP ); N(-1.0, 0.0, 0.0); V(-,-,+); V(-,+,+); V(-,+,-); V(-,-,-); glEnd();
247     glBegin( GL_LINE_LOOP ); N( 0.0,-1.0, 0.0); V(-,-,+); V(-,-,-); V(+,-,-); V(+,-,+); glEnd();
248     glBegin( GL_LINE_LOOP ); N( 0.0, 0.0,-1.0); V(-,-,-); V(-,+,-); V(+,+,-); V(+,-,-); glEnd();
249
250 #   undef V
251 #   undef N
252 }
253
254 /*
255  * Draws a solid cube. Code contributed by Andreas Umbach <marvin@dataway.ch>
256  */
257 void FGAPIENTRY glutSolidCube( GLdouble dSize )
258 {
259     double size = dSize * 0.5;
260
261     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
262
263 #   define V(a,b,c) glVertex3d( a size, b size, c size );
264 #   define N(a,b,c) glNormal3d( a, b, c );
265
266     /* PWO: Again, I dared to convert the code to use macros... */
267     glBegin( GL_QUADS );
268         N( 1.0, 0.0, 0.0); V(+,-,+); V(+,-,-); V(+,+,-); V(+,+,+);
269         N( 0.0, 1.0, 0.0); V(+,+,+); V(+,+,-); V(-,+,-); V(-,+,+);
270         N( 0.0, 0.0, 1.0); V(+,+,+); V(-,+,+); V(-,-,+); V(+,-,+);
271         N(-1.0, 0.0, 0.0); V(-,-,+); V(-,+,+); V(-,+,-); V(-,-,-);
272         N( 0.0,-1.0, 0.0); V(-,-,+); V(-,-,-); V(+,-,-); V(+,-,+);
273         N( 0.0, 0.0,-1.0); V(-,-,-); V(-,+,-); V(+,+,-); V(+,-,-);
274     glEnd();
275
276 #   undef V
277 #   undef N
278 }
279
280
281 /*
282  * Draws a solid sphere
283  */
284 void FGAPIENTRY glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
285 {
286     int i,j;
287
288     /* Adjust z and radius as stacks are drawn. */
289
290     double z0,z1;
291     double r0,r1;
292
293     /* Pre-computed circle */
294
295     double *sint1,*cost1;
296     double *sint2,*cost2;
297
298     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
299
300     fghCircleTable(&sint1,&cost1,-slices);
301     fghCircleTable(&sint2,&cost2,stacks*2);
302
303     /* The top stack is covered with a triangle fan */
304
305     z0 = 1.0;
306     z1 = cost2[(stacks>0)?1:0];
307     r0 = 0.0;
308     r1 = sint2[(stacks>0)?1:0];
309
310     glBegin(GL_TRIANGLE_FAN);
311
312         glNormal3d(0,0,1);
313         glVertex3d(0,0,radius);
314
315         for (j=slices; j>=0; j--)
316         {
317             glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
318             glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
319         }
320
321     glEnd();
322
323     /* Cover each stack with a quad strip, except the top and bottom stacks */
324
325     for( i=1; i<stacks-1; i++ )
326     {
327         z0 = z1; z1 = cost2[i+1];
328         r0 = r1; r1 = sint2[i+1];
329
330         glBegin(GL_QUAD_STRIP);
331
332             for(j=0; j<=slices; j++)
333             {
334                 glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
335                 glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
336                 glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
337                 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
338             }
339
340         glEnd();
341     }
342
343     /* The bottom stack is covered with a triangle fan */
344
345     z0 = z1;
346     r0 = r1;
347
348     glBegin(GL_TRIANGLE_FAN);
349
350         glNormal3d(0,0,-1);
351         glVertex3d(0,0,-radius);
352
353         for (j=0; j<=slices; j++)
354         {
355             glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
356             glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
357         }
358
359     glEnd();
360
361     /* Release sin and cos tables */
362
363     free(sint1);
364     free(cost1);
365     free(sint2);
366     free(cost2);
367 }
368
369 /*
370  * Draws a wire sphere
371  */
372 void FGAPIENTRY glutWireSphere(GLdouble radius, GLint slices, GLint stacks)
373 {
374     int i,j;
375
376     /* Adjust z and radius as stacks and slices are drawn. */
377
378     double r;
379     double x,y,z;
380
381     /* Pre-computed circle */
382
383     double *sint1,*cost1;
384     double *sint2,*cost2;
385
386     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
387
388     fghCircleTable(&sint1,&cost1,-slices  );
389     fghCircleTable(&sint2,&cost2, stacks*2);
390
391     /* Draw a line loop for each stack */
392
393     for (i=1; i<stacks; i++)
394     {
395         z = cost2[i];
396         r = sint2[i];
397
398         glBegin(GL_LINE_LOOP);
399
400             for(j=0; j<=slices; j++)
401             {
402                 x = cost1[j];
403                 y = sint1[j];
404
405                 glNormal3d(x,y,z);
406                 glVertex3d(x*r*radius,y*r*radius,z*radius);
407             }
408
409         glEnd();
410     }
411
412     /* Draw a line loop for each slice */
413
414     for (i=0; i<slices; i++)
415     {
416         glBegin(GL_LINE_STRIP);
417
418             for(j=0; j<=stacks; j++)
419             {
420                 x = cost1[i]*sint2[j];
421                 y = sint1[i]*sint2[j];
422                 z = cost2[j];
423
424                 glNormal3d(x,y,z);
425                 glVertex3d(x*radius,y*radius,z*radius);
426             }
427
428         glEnd();
429     }
430
431     /* Release sin and cos tables */
432
433     free(sint1);
434     free(cost1);
435     free(sint2);
436     free(cost2);
437 }
438
439 /*
440  * Draws a solid cone
441  */
442 void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
443 {
444     int i,j;
445
446     /* Step in z and radius as stacks are drawn. */
447
448     double z0,z1;
449     double r0,r1;
450
451     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
452     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
453
454     /* Scaling factors for vertex normals */
455
456     const double cosn = ( height / sqrt ( height * height + base * base ));
457     const double sinn = ( base   / sqrt ( height * height + base * base ));
458
459     /* Pre-computed circle */
460
461     double *sint,*cost;
462
463     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
464
465     fghCircleTable(&sint,&cost,-slices);
466
467     /* Cover the circular base with a triangle fan... */
468
469     z0 = 0.0;
470     z1 = zStep;
471
472     r0 = base;
473     r1 = r0 - rStep;
474
475     glBegin(GL_TRIANGLE_FAN);
476
477         glNormal3d(0.0,0.0,-1.0);
478         glVertex3d(0.0,0.0, z0 );
479
480         for (j=0; j<=slices; j++)
481             glVertex3d(cost[j]*r0, sint[j]*r0, z0);
482
483     glEnd();
484
485     /* Cover each stack with a quad strip, except the top stack */
486
487     for( i=0; i<stacks-1; i++ )
488     {
489         glBegin(GL_QUAD_STRIP);
490
491             for(j=0; j<=slices; j++)
492             {
493                 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
494                 glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
495                 glVertex3d(cost[j]*r1,   sint[j]*r1,   z1  );
496             }
497
498             z0 = z1; z1 += zStep;
499             r0 = r1; r1 -= rStep;
500
501         glEnd();
502     }
503
504     /* The top stack is covered with individual triangles */
505
506     glBegin(GL_TRIANGLES);
507
508         glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
509
510         for (j=0; j<slices; j++)
511         {
512             glVertex3d(cost[j+0]*r0,   sint[j+0]*r0,   z0    );
513             glVertex3d(0,              0,              height);
514             glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn  );
515             glVertex3d(cost[j+1]*r0,   sint[j+1]*r0,   z0    );
516         }
517
518     glEnd();
519
520     /* Release sin and cos tables */
521
522     free(sint);
523     free(cost);
524 }
525
526 /*
527  * Draws a wire cone
528  */
529 void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks)
530 {
531     int i,j;
532
533     /* Step in z and radius as stacks are drawn. */
534
535     double z = 0.0;
536     double r = base;
537
538     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
539     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
540
541     /* Scaling factors for vertex normals */
542
543     const double cosn = ( height / sqrt ( height * height + base * base ));
544     const double sinn = ( base   / sqrt ( height * height + base * base ));
545
546     /* Pre-computed circle */
547
548     double *sint,*cost;
549
550     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
551
552     fghCircleTable(&sint,&cost,-slices);
553
554     /* Draw the stacks... */
555
556     for (i=0; i<stacks; i++)
557     {
558         glBegin(GL_LINE_LOOP);
559
560             for( j=0; j<slices; j++ )
561             {
562                 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
563                 glVertex3d(cost[j]*r,    sint[j]*r,    z   );
564             }
565
566         glEnd();
567
568         z += zStep;
569         r -= rStep;
570     }
571
572     /* Draw the slices */
573
574     r = base;
575
576     glBegin(GL_LINES);
577
578         for (j=0; j<slices; j++)
579         {
580             glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn  );
581             glVertex3d(cost[j]*r,    sint[j]*r,    0.0   );
582             glVertex3d(0.0,          0.0,          height);
583         }
584
585     glEnd();
586
587     /* Release sin and cos tables */
588
589     free(sint);
590     free(cost);
591 }
592
593
594 /*
595  * Draws a solid cylinder
596  */
597 void FGAPIENTRY glutSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
598 {
599     int i,j;
600
601     /* Step in z and radius as stacks are drawn. */
602
603     double z0,z1;
604     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
605
606     /* Pre-computed circle */
607
608     double *sint,*cost;
609
610     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
611
612     fghCircleTable(&sint,&cost,-slices);
613
614     /* Cover the base and top */
615
616     glBegin(GL_TRIANGLE_FAN);
617         glNormal3d(0.0, 0.0, -1.0 );
618         glVertex3d(0.0, 0.0,  0.0 );
619         for (j=0; j<=slices; j++)
620           glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
621     glEnd();
622
623     glBegin(GL_TRIANGLE_FAN);
624         glNormal3d(0.0, 0.0, 1.0   );
625         glVertex3d(0.0, 0.0, height);
626         for (j=slices; j>=0; j--)
627           glVertex3d(cost[j]*radius, sint[j]*radius, height);
628     glEnd();
629
630     /* Do the stacks */
631
632     z0 = 0.0;
633     z1 = zStep;
634
635     for (i=1; i<=stacks; i++)
636     {
637         if (i==stacks)
638             z1 = height;
639
640         glBegin(GL_QUAD_STRIP);
641             for (j=0; j<=slices; j++ )
642             {
643                 glNormal3d(cost[j],        sint[j],        0.0 );
644                 glVertex3d(cost[j]*radius, sint[j]*radius, z0  );
645                 glVertex3d(cost[j]*radius, sint[j]*radius, z1  );
646             }
647         glEnd();
648
649         z0 = z1; z1 += zStep;
650     }
651
652     /* Release sin and cos tables */
653
654     free(sint);
655     free(cost);
656 }
657
658 /*
659  * Draws a wire cylinder
660  */
661 void FGAPIENTRY glutWireCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
662 {
663     int i,j;
664
665     /* Step in z and radius as stacks are drawn. */
666
667           double z = 0.0;
668     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
669
670     /* Pre-computed circle */
671
672     double *sint,*cost;
673
674     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
675
676     fghCircleTable(&sint,&cost,-slices);
677
678     /* Draw the stacks... */
679
680     for (i=0; i<=stacks; i++)
681     {
682         if (i==stacks)
683             z = height;
684
685         glBegin(GL_LINE_LOOP);
686
687             for( j=0; j<slices; j++ )
688             {
689                 glNormal3d(cost[j],        sint[j],        0.0);
690                 glVertex3d(cost[j]*radius, sint[j]*radius, z  );
691             }
692
693         glEnd();
694
695         z += zStep;
696     }
697
698     /* Draw the slices */
699
700     glBegin(GL_LINES);
701
702         for (j=0; j<slices; j++)
703         {
704             glNormal3d(cost[j],        sint[j],        0.0   );
705             glVertex3d(cost[j]*radius, sint[j]*radius, 0.0   );
706             glVertex3d(cost[j]*radius, sint[j]*radius, height);
707         }
708
709     glEnd();
710
711     /* Release sin and cos tables */
712
713     free(sint);
714     free(cost);
715 }
716
717 /*
718  * Draws a wire torus
719  */
720 void FGAPIENTRY glutWireTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
721 {
722   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
723   double *vertex, *normal;
724   int    i, j;
725   double spsi, cpsi, sphi, cphi ;
726
727   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
728
729   if ( nSides < 1 ) nSides = 1;
730   if ( nRings < 1 ) nRings = 1;
731
732   /* Allocate the vertices array */
733   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
734   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
735
736   glPushMatrix();
737
738   dpsi =  2.0 * M_PI / (double)nRings ;
739   dphi = -2.0 * M_PI / (double)nSides ;
740   psi  = 0.0;
741
742   for( j=0; j<nRings; j++ )
743   {
744     cpsi = cos ( psi ) ;
745     spsi = sin ( psi ) ;
746     phi = 0.0;
747
748     for( i=0; i<nSides; i++ )
749     {
750       int offset = 3 * ( j * nSides + i ) ;
751       cphi = cos ( phi ) ;
752       sphi = sin ( phi ) ;
753       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
754       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
755       *(vertex + offset + 2) =                    sphi * iradius  ;
756       *(normal + offset + 0) = cpsi * cphi ;
757       *(normal + offset + 1) = spsi * cphi ;
758       *(normal + offset + 2) =        sphi ;
759       phi += dphi;
760     }
761
762     psi += dpsi;
763   }
764
765   for( i=0; i<nSides; i++ )
766   {
767     glBegin( GL_LINE_LOOP );
768
769     for( j=0; j<nRings; j++ )
770     {
771       int offset = 3 * ( j * nSides + i ) ;
772       glNormal3dv( normal + offset );
773       glVertex3dv( vertex + offset );
774     }
775
776     glEnd();
777   }
778
779   for( j=0; j<nRings; j++ )
780   {
781     glBegin(GL_LINE_LOOP);
782
783     for( i=0; i<nSides; i++ )
784     {
785       int offset = 3 * ( j * nSides + i ) ;
786       glNormal3dv( normal + offset );
787       glVertex3dv( vertex + offset );
788     }
789
790     glEnd();
791   }
792
793   free ( vertex ) ;
794   free ( normal ) ;
795   glPopMatrix();
796 }
797
798 /*
799  * Draws a solid torus
800  */
801 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
802 {
803   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
804   double *vertex, *normal;
805   int    i, j;
806   double spsi, cpsi, sphi, cphi ;
807
808   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
809
810   if ( nSides < 1 ) nSides = 1;
811   if ( nRings < 1 ) nRings = 1;
812
813   /* Increment the number of sides and rings to allow for one more point than surface */
814   nSides ++ ;
815   nRings ++ ;
816
817   /* Allocate the vertices array */
818   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
819   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
820
821   glPushMatrix();
822
823   dpsi =  2.0 * M_PI / (double)(nRings - 1) ;
824   dphi = -2.0 * M_PI / (double)(nSides - 1) ;
825   psi  = 0.0;
826
827   for( j=0; j<nRings; j++ )
828   {
829     cpsi = cos ( psi ) ;
830     spsi = sin ( psi ) ;
831     phi = 0.0;
832
833     for( i=0; i<nSides; i++ )
834     {
835       int offset = 3 * ( j * nSides + i ) ;
836       cphi = cos ( phi ) ;
837       sphi = sin ( phi ) ;
838       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
839       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
840       *(vertex + offset + 2) =                    sphi * iradius  ;
841       *(normal + offset + 0) = cpsi * cphi ;
842       *(normal + offset + 1) = spsi * cphi ;
843       *(normal + offset + 2) =        sphi ;
844       phi += dphi;
845     }
846
847     psi += dpsi;
848   }
849
850     glBegin( GL_QUADS );
851   for( i=0; i<nSides-1; i++ )
852   {
853     for( j=0; j<nRings-1; j++ )
854     {
855       int offset = 3 * ( j * nSides + i ) ;
856       glNormal3dv( normal + offset );
857       glVertex3dv( vertex + offset );
858       glNormal3dv( normal + offset + 3 );
859       glVertex3dv( vertex + offset + 3 );
860       glNormal3dv( normal + offset + 3 * nSides + 3 );
861       glVertex3dv( vertex + offset + 3 * nSides + 3 );
862       glNormal3dv( normal + offset + 3 * nSides );
863       glVertex3dv( vertex + offset + 3 * nSides );
864     }
865   }
866
867   glEnd();
868
869   free ( vertex ) ;
870   free ( normal ) ;
871   glPopMatrix();
872 }
873
874 /*
875  *
876  */
877 void FGAPIENTRY glutWireDodecahedron( void )
878 {
879   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireDodecahedron" );
880
881   /* Magic Numbers:  It is possible to create a dodecahedron by attaching two pentagons to each face of
882    * of a cube.  The coordinates of the points are:
883    *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
884    * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2  or
885    *       x = 0.61803398875 and z = 1.61803398875.
886    */
887   glBegin ( GL_LINE_LOOP ) ;
888   glNormal3d (  0.0,  0.525731112119,  0.850650808354 ) ; glVertex3d (  0.0,  1.61803398875,  0.61803398875 ) ; glVertex3d ( -1.0,  1.0,  1.0 ) ; glVertex3d ( -0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d (  0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d (  1.0,  1.0,  1.0 ) ;
889   glEnd () ;
890   glBegin ( GL_LINE_LOOP ) ;
891   glNormal3d (  0.0,  0.525731112119, -0.850650808354 ) ; glVertex3d (  0.0,  1.61803398875, -0.61803398875 ) ; glVertex3d (  1.0,  1.0, -1.0 ) ; glVertex3d (  0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d ( -0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d ( -1.0,  1.0, -1.0 ) ;
892   glEnd () ;
893   glBegin ( GL_LINE_LOOP ) ;
894   glNormal3d (  0.0, -0.525731112119,  0.850650808354 ) ; glVertex3d (  0.0, -1.61803398875,  0.61803398875 ) ; glVertex3d (  1.0, -1.0,  1.0 ) ; glVertex3d (  0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d ( -0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d ( -1.0, -1.0,  1.0 ) ;
895   glEnd () ;
896   glBegin ( GL_LINE_LOOP ) ;
897   glNormal3d (  0.0, -0.525731112119, -0.850650808354 ) ; glVertex3d (  0.0, -1.61803398875, -0.61803398875 ) ; glVertex3d ( -1.0, -1.0, -1.0 ) ; glVertex3d ( -0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d (  0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d (  1.0, -1.0, -1.0 ) ;
898   glEnd () ;
899
900   glBegin ( GL_LINE_LOOP ) ;
901   glNormal3d (  0.850650808354,  0.0,  0.525731112119 ) ; glVertex3d (  0.61803398875,  0.0,  1.61803398875 ) ; glVertex3d (  1.0, -1.0,  1.0 ) ; glVertex3d (  1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d (  1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d (  1.0,  1.0,  1.0 ) ;
902   glEnd () ;
903   glBegin ( GL_LINE_LOOP ) ;
904   glNormal3d ( -0.850650808354,  0.0,  0.525731112119 ) ; glVertex3d ( -0.61803398875,  0.0,  1.61803398875 ) ; glVertex3d ( -1.0,  1.0,  1.0 ) ; glVertex3d ( -1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d ( -1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d ( -1.0, -1.0,  1.0 ) ;
905   glEnd () ;
906   glBegin ( GL_LINE_LOOP ) ;
907   glNormal3d (  0.850650808354,  0.0, -0.525731112119 ) ; glVertex3d (  0.61803398875,  0.0, -1.61803398875 ) ; glVertex3d (  1.0,  1.0, -1.0 ) ; glVertex3d (  1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d (  1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d (  1.0, -1.0, -1.0 ) ;
908   glEnd () ;
909   glBegin ( GL_LINE_LOOP ) ;
910   glNormal3d ( -0.850650808354,  0.0, -0.525731112119 ) ; glVertex3d ( -0.61803398875,  0.0, -1.61803398875 ) ; glVertex3d ( -1.0, -1.0, -1.0 ) ; glVertex3d ( -1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d ( -1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d ( -1.0,  1.0, -1.0 ) ;
911   glEnd () ;
912
913   glBegin ( GL_LINE_LOOP ) ;
914   glNormal3d (  0.525731112119,  0.850650808354,  0.0 ) ; glVertex3d (  1.61803398875,  0.61803398875,  0.0 ) ; glVertex3d (  1.0,  1.0, -1.0 ) ; glVertex3d ( 0.0,  1.61803398875, -0.61803398875 ) ; glVertex3d ( 0.0,  1.61803398875,  0.61803398875 ) ; glVertex3d (  1.0,  1.0,  1.0 ) ;
915   glEnd () ;
916   glBegin ( GL_LINE_LOOP ) ;
917   glNormal3d (  0.525731112119, -0.850650808354,  0.0 ) ; glVertex3d (  1.61803398875, -0.61803398875,  0.0 ) ; glVertex3d (  1.0, -1.0,  1.0 ) ; glVertex3d ( 0.0, -1.61803398875,  0.61803398875 ) ; glVertex3d ( 0.0, -1.61803398875, -0.61803398875 ) ; glVertex3d (  1.0, -1.0, -1.0 ) ;
918   glEnd () ;
919   glBegin ( GL_LINE_LOOP ) ;
920   glNormal3d ( -0.525731112119,  0.850650808354,  0.0 ) ; glVertex3d ( -1.61803398875,  0.61803398875,  0.0 ) ; glVertex3d ( -1.0,  1.0,  1.0 ) ; glVertex3d ( 0.0,  1.61803398875,  0.61803398875 ) ; glVertex3d ( 0.0,  1.61803398875, -0.61803398875 ) ; glVertex3d ( -1.0,  1.0, -1.0 ) ;
921   glEnd () ;
922   glBegin ( GL_LINE_LOOP ) ;
923   glNormal3d ( -0.525731112119, -0.850650808354,  0.0 ) ; glVertex3d ( -1.61803398875, -0.61803398875,  0.0 ) ; glVertex3d ( -1.0, -1.0, -1.0 ) ; glVertex3d ( 0.0, -1.61803398875, -0.61803398875 ) ; glVertex3d ( 0.0, -1.61803398875,  0.61803398875 ) ; glVertex3d ( -1.0, -1.0,  1.0 ) ;
924   glEnd () ;
925 }
926
927 /*
928  *
929  */
930 void FGAPIENTRY glutSolidDodecahedron( void )
931 {
932   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidDodecahedron" );
933
934   /* Magic Numbers:  It is possible to create a dodecahedron by attaching two pentagons to each face of
935    * of a cube.  The coordinates of the points are:
936    *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
937    * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
938    *       x = 0.61803398875 and z = 1.61803398875.
939    */
940   glBegin ( GL_POLYGON ) ;
941   glNormal3d (  0.0,  0.525731112119,  0.850650808354 ) ; glVertex3d (  0.0,  1.61803398875,  0.61803398875 ) ; glVertex3d ( -1.0,  1.0,  1.0 ) ; glVertex3d ( -0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d (  0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d (  1.0,  1.0,  1.0 ) ;
942   glEnd () ;
943   glBegin ( GL_POLYGON ) ;
944   glNormal3d (  0.0,  0.525731112119, -0.850650808354 ) ; glVertex3d (  0.0,  1.61803398875, -0.61803398875 ) ; glVertex3d (  1.0,  1.0, -1.0 ) ; glVertex3d (  0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d ( -0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d ( -1.0,  1.0, -1.0 ) ;
945   glEnd () ;
946   glBegin ( GL_POLYGON ) ;
947   glNormal3d (  0.0, -0.525731112119,  0.850650808354 ) ; glVertex3d (  0.0, -1.61803398875,  0.61803398875 ) ; glVertex3d (  1.0, -1.0,  1.0 ) ; glVertex3d (  0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d ( -0.61803398875, 0.0,  1.61803398875 ) ; glVertex3d ( -1.0, -1.0,  1.0 ) ;
948   glEnd () ;
949   glBegin ( GL_POLYGON ) ;
950   glNormal3d (  0.0, -0.525731112119, -0.850650808354 ) ; glVertex3d (  0.0, -1.61803398875, -0.61803398875 ) ; glVertex3d ( -1.0, -1.0, -1.0 ) ; glVertex3d ( -0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d (  0.61803398875, 0.0, -1.61803398875 ) ; glVertex3d (  1.0, -1.0, -1.0 ) ;
951   glEnd () ;
952
953   glBegin ( GL_POLYGON ) ;
954   glNormal3d (  0.850650808354,  0.0,  0.525731112119 ) ; glVertex3d (  0.61803398875,  0.0,  1.61803398875 ) ; glVertex3d (  1.0, -1.0,  1.0 ) ; glVertex3d (  1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d (  1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d (  1.0,  1.0,  1.0 ) ;
955   glEnd () ;
956   glBegin ( GL_POLYGON ) ;
957   glNormal3d ( -0.850650808354,  0.0,  0.525731112119 ) ; glVertex3d ( -0.61803398875,  0.0,  1.61803398875 ) ; glVertex3d ( -1.0,  1.0,  1.0 ) ; glVertex3d ( -1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d ( -1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d ( -1.0, -1.0,  1.0 ) ;
958   glEnd () ;
959   glBegin ( GL_POLYGON ) ;
960   glNormal3d (  0.850650808354,  0.0, -0.525731112119 ) ; glVertex3d (  0.61803398875,  0.0, -1.61803398875 ) ; glVertex3d (  1.0,  1.0, -1.0 ) ; glVertex3d (  1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d (  1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d (  1.0, -1.0, -1.0 ) ;
961   glEnd () ;
962   glBegin ( GL_POLYGON ) ;
963   glNormal3d ( -0.850650808354,  0.0, -0.525731112119 ) ; glVertex3d ( -0.61803398875,  0.0, -1.61803398875 ) ; glVertex3d ( -1.0, -1.0, -1.0 ) ; glVertex3d ( -1.61803398875, -0.61803398875, 0.0 ) ; glVertex3d ( -1.61803398875,  0.61803398875, 0.0 ) ; glVertex3d ( -1.0,  1.0, -1.0 ) ;
964   glEnd () ;
965
966   glBegin ( GL_POLYGON ) ;
967   glNormal3d (  0.525731112119,  0.850650808354,  0.0 ) ; glVertex3d (  1.61803398875,  0.61803398875,  0.0 ) ; glVertex3d (  1.0,  1.0, -1.0 ) ; glVertex3d ( 0.0,  1.61803398875, -0.61803398875 ) ; glVertex3d ( 0.0,  1.61803398875,  0.61803398875 ) ; glVertex3d (  1.0,  1.0,  1.0 ) ;
968   glEnd () ;
969   glBegin ( GL_POLYGON ) ;
970   glNormal3d (  0.525731112119, -0.850650808354,  0.0 ) ; glVertex3d (  1.61803398875, -0.61803398875,  0.0 ) ; glVertex3d (  1.0, -1.0,  1.0 ) ; glVertex3d ( 0.0, -1.61803398875,  0.61803398875 ) ; glVertex3d ( 0.0, -1.61803398875, -0.61803398875 ) ; glVertex3d (  1.0, -1.0, -1.0 ) ;
971   glEnd () ;
972   glBegin ( GL_POLYGON ) ;
973   glNormal3d ( -0.525731112119,  0.850650808354,  0.0 ) ; glVertex3d ( -1.61803398875,  0.61803398875,  0.0 ) ; glVertex3d ( -1.0,  1.0,  1.0 ) ; glVertex3d ( 0.0,  1.61803398875,  0.61803398875 ) ; glVertex3d ( 0.0,  1.61803398875, -0.61803398875 ) ; glVertex3d ( -1.0,  1.0, -1.0 ) ;
974   glEnd () ;
975   glBegin ( GL_POLYGON ) ;
976   glNormal3d ( -0.525731112119, -0.850650808354,  0.0 ) ; glVertex3d ( -1.61803398875, -0.61803398875,  0.0 ) ; glVertex3d ( -1.0, -1.0, -1.0 ) ; glVertex3d ( 0.0, -1.61803398875, -0.61803398875 ) ; glVertex3d ( 0.0, -1.61803398875,  0.61803398875 ) ; glVertex3d ( -1.0, -1.0,  1.0 ) ;
977   glEnd () ;
978 }
979
980 /*
981  *
982  */
983 void FGAPIENTRY glutWireOctahedron( void )
984 {
985   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireOctahedron" );
986
987 #define RADIUS    1.0f
988   glBegin( GL_LINE_LOOP );
989     glNormal3d( 0.577350269189, 0.577350269189, 0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, RADIUS, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS );
990     glNormal3d( 0.577350269189, 0.577350269189,-0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS ); glVertex3d( 0.0, RADIUS, 0.0 );
991     glNormal3d( 0.577350269189,-0.577350269189, 0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS ); glVertex3d( 0.0,-RADIUS, 0.0 );
992     glNormal3d( 0.577350269189,-0.577350269189,-0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0,-RADIUS, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS );
993     glNormal3d(-0.577350269189, 0.577350269189, 0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS ); glVertex3d( 0.0, RADIUS, 0.0 );
994     glNormal3d(-0.577350269189, 0.577350269189,-0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, RADIUS, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS );
995     glNormal3d(-0.577350269189,-0.577350269189, 0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0,-RADIUS, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS );
996     glNormal3d(-0.577350269189,-0.577350269189,-0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS ); glVertex3d( 0.0,-RADIUS, 0.0 );
997   glEnd();
998 #undef RADIUS
999 }
1000
1001 /*
1002  *
1003  */
1004 void FGAPIENTRY glutSolidOctahedron( void )
1005 {
1006   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidOctahedron" );
1007
1008 #define RADIUS    1.0f
1009   glBegin( GL_TRIANGLES );
1010     glNormal3d( 0.577350269189, 0.577350269189, 0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, RADIUS, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS );
1011     glNormal3d( 0.577350269189, 0.577350269189,-0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS ); glVertex3d( 0.0, RADIUS, 0.0 );
1012     glNormal3d( 0.577350269189,-0.577350269189, 0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS ); glVertex3d( 0.0,-RADIUS, 0.0 );
1013     glNormal3d( 0.577350269189,-0.577350269189,-0.577350269189); glVertex3d( RADIUS, 0.0, 0.0 ); glVertex3d( 0.0,-RADIUS, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS );
1014     glNormal3d(-0.577350269189, 0.577350269189, 0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS ); glVertex3d( 0.0, RADIUS, 0.0 );
1015     glNormal3d(-0.577350269189, 0.577350269189,-0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, RADIUS, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS );
1016     glNormal3d(-0.577350269189,-0.577350269189, 0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0,-RADIUS, 0.0 ); glVertex3d( 0.0, 0.0, RADIUS );
1017     glNormal3d(-0.577350269189,-0.577350269189,-0.577350269189); glVertex3d(-RADIUS, 0.0, 0.0 ); glVertex3d( 0.0, 0.0,-RADIUS ); glVertex3d( 0.0,-RADIUS, 0.0 );
1018   glEnd();
1019 #undef RADIUS
1020 }
1021
1022 /*
1023  *
1024  */
1025 static double icos_r[12][3] = {
1026     {  1.0,             0.0,             0.0            },
1027     {  0.447213595500,  0.894427191000,  0.0            },
1028     {  0.447213595500,  0.276393202252,  0.850650808354 },
1029     {  0.447213595500, -0.723606797748,  0.525731112119 },
1030     {  0.447213595500, -0.723606797748, -0.525731112119 },
1031     {  0.447213595500,  0.276393202252, -0.850650808354 },
1032     { -0.447213595500, -0.894427191000,  0.0 },
1033     { -0.447213595500, -0.276393202252,  0.850650808354 },
1034     { -0.447213595500,  0.723606797748,  0.525731112119 },
1035     { -0.447213595500,  0.723606797748, -0.525731112119 },
1036     { -0.447213595500, -0.276393202252, -0.850650808354 },
1037     { -1.0,             0.0,             0.0            }
1038 };
1039
1040 static int icos_v [20][3] = {
1041     {  0,  1,  2 },
1042     {  0,  2,  3 },
1043     {  0,  3,  4 },
1044     {  0,  4,  5 },
1045     {  0,  5,  1 },
1046     {  1,  8,  2 },
1047     {  2,  7,  3 },
1048     {  3,  6,  4 },
1049     {  4, 10,  5 },
1050     {  5,  9,  1 },
1051     {  1,  9,  8 },
1052     {  2,  8,  7 },
1053     {  3,  7,  6 },
1054     {  4,  6, 10 },
1055     {  5, 10,  9 },
1056     { 11,  9, 10 },
1057     { 11,  8,  9 },
1058     { 11,  7,  8 },
1059     { 11,  6,  7 },
1060     { 11, 10,  6 }
1061 };
1062
1063 void FGAPIENTRY glutWireIcosahedron( void )
1064 {
1065   int i ;
1066
1067   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireIcosahedron" );
1068
1069   for ( i = 0; i < 20; i++ )
1070   {
1071     double normal[3] ;
1072     normal[0] = ( icos_r[icos_v[i][1]][1] - icos_r[icos_v[i][0]][1] ) * ( icos_r[icos_v[i][2]][2] - icos_r[icos_v[i][0]][2] ) - ( icos_r[icos_v[i][1]][2] - icos_r[icos_v[i][0]][2] ) * ( icos_r[icos_v[i][2]][1] - icos_r[icos_v[i][0]][1] ) ;
1073     normal[1] = ( icos_r[icos_v[i][1]][2] - icos_r[icos_v[i][0]][2] ) * ( icos_r[icos_v[i][2]][0] - icos_r[icos_v[i][0]][0] ) - ( icos_r[icos_v[i][1]][0] - icos_r[icos_v[i][0]][0] ) * ( icos_r[icos_v[i][2]][2] - icos_r[icos_v[i][0]][2] ) ;
1074     normal[2] = ( icos_r[icos_v[i][1]][0] - icos_r[icos_v[i][0]][0] ) * ( icos_r[icos_v[i][2]][1] - icos_r[icos_v[i][0]][1] ) - ( icos_r[icos_v[i][1]][1] - icos_r[icos_v[i][0]][1] ) * ( icos_r[icos_v[i][2]][0] - icos_r[icos_v[i][0]][0] ) ;
1075     glBegin ( GL_LINE_LOOP ) ;
1076       glNormal3dv ( normal ) ;
1077       glVertex3dv ( icos_r[icos_v[i][0]] ) ;
1078       glVertex3dv ( icos_r[icos_v[i][1]] ) ;
1079       glVertex3dv ( icos_r[icos_v[i][2]] ) ;
1080     glEnd () ;
1081   }
1082 }
1083
1084 /*
1085  *
1086  */
1087 void FGAPIENTRY glutSolidIcosahedron( void )
1088 {
1089   int i ;
1090
1091   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidIcosahedron" );
1092
1093   glBegin ( GL_TRIANGLES ) ;
1094   for ( i = 0; i < 20; i++ )
1095   {
1096     double normal[3] ;
1097     normal[0] = ( icos_r[icos_v[i][1]][1] - icos_r[icos_v[i][0]][1] ) * ( icos_r[icos_v[i][2]][2] - icos_r[icos_v[i][0]][2] ) - ( icos_r[icos_v[i][1]][2] - icos_r[icos_v[i][0]][2] ) * ( icos_r[icos_v[i][2]][1] - icos_r[icos_v[i][0]][1] ) ;
1098     normal[1] = ( icos_r[icos_v[i][1]][2] - icos_r[icos_v[i][0]][2] ) * ( icos_r[icos_v[i][2]][0] - icos_r[icos_v[i][0]][0] ) - ( icos_r[icos_v[i][1]][0] - icos_r[icos_v[i][0]][0] ) * ( icos_r[icos_v[i][2]][2] - icos_r[icos_v[i][0]][2] ) ;
1099     normal[2] = ( icos_r[icos_v[i][1]][0] - icos_r[icos_v[i][0]][0] ) * ( icos_r[icos_v[i][2]][1] - icos_r[icos_v[i][0]][1] ) - ( icos_r[icos_v[i][1]][1] - icos_r[icos_v[i][0]][1] ) * ( icos_r[icos_v[i][2]][0] - icos_r[icos_v[i][0]][0] ) ;
1100       glNormal3dv ( normal ) ;
1101       glVertex3dv ( icos_r[icos_v[i][0]] ) ;
1102       glVertex3dv ( icos_r[icos_v[i][1]] ) ;
1103       glVertex3dv ( icos_r[icos_v[i][2]] ) ;
1104   }
1105
1106   glEnd () ;
1107 }
1108
1109 /*
1110  *
1111  */
1112 static double rdod_r[14][3] = {
1113     {  0.0,             0.0,             1.0 },
1114     {  0.707106781187,  0.000000000000,  0.5 },
1115     {  0.000000000000,  0.707106781187,  0.5 },
1116     { -0.707106781187,  0.000000000000,  0.5 },
1117     {  0.000000000000, -0.707106781187,  0.5 },
1118     {  0.707106781187,  0.707106781187,  0.0 },
1119     { -0.707106781187,  0.707106781187,  0.0 },
1120     { -0.707106781187, -0.707106781187,  0.0 },
1121     {  0.707106781187, -0.707106781187,  0.0 },
1122     {  0.707106781187,  0.000000000000, -0.5 },
1123     {  0.000000000000,  0.707106781187, -0.5 },
1124     { -0.707106781187,  0.000000000000, -0.5 },
1125     {  0.000000000000, -0.707106781187, -0.5 },
1126     {  0.0,             0.0,            -1.0 }
1127 } ;
1128
1129 static int rdod_v [12][4] = {
1130     { 0,  1,  5,  2 },
1131     { 0,  2,  6,  3 },
1132     { 0,  3,  7,  4 },
1133     { 0,  4,  8,  1 },
1134     { 5, 10,  6,  2 },
1135     { 6, 11,  7,  3 },
1136     { 7, 12,  8,  4 },
1137     { 8,  9,  5,  1 },
1138     { 5,  9, 13, 10 },
1139     { 6, 10, 13, 11 },
1140     { 7, 11, 13, 12 },
1141     { 8, 12, 13,  9 }
1142 };
1143
1144 static double rdod_n[12][3] = {
1145     {  0.353553390594,  0.353553390594,  0.5 },
1146     { -0.353553390594,  0.353553390594,  0.5 },
1147     { -0.353553390594, -0.353553390594,  0.5 },
1148     {  0.353553390594, -0.353553390594,  0.5 },
1149     {  0.000000000000,  1.000000000000,  0.0 },
1150     { -1.000000000000,  0.000000000000,  0.0 },
1151     {  0.000000000000, -1.000000000000,  0.0 },
1152     {  1.000000000000,  0.000000000000,  0.0 },
1153     {  0.353553390594,  0.353553390594, -0.5 },
1154     { -0.353553390594,  0.353553390594, -0.5 },
1155     { -0.353553390594, -0.353553390594, -0.5 },
1156     {  0.353553390594, -0.353553390594, -0.5 }
1157 };
1158
1159 void FGAPIENTRY glutWireRhombicDodecahedron( void )
1160 {
1161   int i ;
1162
1163   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireRhombicDodecahedron" );
1164
1165   for ( i = 0; i < 12; i++ )
1166   {
1167     glBegin ( GL_LINE_LOOP ) ;
1168       glNormal3dv ( rdod_n[i] ) ;
1169       glVertex3dv ( rdod_r[rdod_v[i][0]] ) ;
1170       glVertex3dv ( rdod_r[rdod_v[i][1]] ) ;
1171       glVertex3dv ( rdod_r[rdod_v[i][2]] ) ;
1172       glVertex3dv ( rdod_r[rdod_v[i][3]] ) ;
1173     glEnd () ;
1174   }
1175 }
1176
1177 /*
1178  *
1179  */
1180 void FGAPIENTRY glutSolidRhombicDodecahedron( void )
1181 {
1182   int i ;
1183
1184   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidRhombicDodecahedron" );
1185
1186   glBegin ( GL_QUADS ) ;
1187   for ( i = 0; i < 12; i++ )
1188   {
1189       glNormal3dv ( rdod_n[i] ) ;
1190       glVertex3dv ( rdod_r[rdod_v[i][0]] ) ;
1191       glVertex3dv ( rdod_r[rdod_v[i][1]] ) ;
1192       glVertex3dv ( rdod_r[rdod_v[i][2]] ) ;
1193       glVertex3dv ( rdod_r[rdod_v[i][3]] ) ;
1194   }
1195
1196   glEnd () ;
1197 }
1198
1199 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1200 {
1201   int i, j ;
1202
1203   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1204
1205   if ( num_levels == 0 )
1206   {
1207
1208     for ( i = 0 ; i < TETR_NUM_FACES ; i++ )
1209     {
1210       glBegin ( GL_LINE_LOOP ) ;
1211       glNormal3d ( -tet_r[i][0], -tet_r[i][1], -tet_r[i][2] ) ;
1212       for ( j = 0; j < 3; j++ )
1213       {
1214         double x = offset[0] + scale * tet_r[tet_i[i][j]][0] ;
1215         double y = offset[1] + scale * tet_r[tet_i[i][j]][1] ;
1216         double z = offset[2] + scale * tet_r[tet_i[i][j]][2] ;
1217         glVertex3d ( x, y, z ) ;
1218       }
1219
1220       glEnd () ;
1221     }
1222   }
1223   else if ( num_levels > 0 )
1224   {
1225     GLdouble local_offset[3] ;  /* Use a local variable to avoid buildup of roundoff errors */
1226     num_levels -- ;
1227     scale /= 2.0 ;
1228     for ( i = 0 ; i < TETR_NUM_FACES ; i++ )
1229     {
1230       local_offset[0] = offset[0] + scale * tet_r[i][0] ;
1231       local_offset[1] = offset[1] + scale * tet_r[i][1] ;
1232       local_offset[2] = offset[2] + scale * tet_r[i][2] ;
1233       glutWireSierpinskiSponge ( num_levels, local_offset, scale ) ;
1234     }
1235   }
1236 }
1237
1238 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1239 {
1240   int i, j ;
1241
1242   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1243
1244   if ( num_levels == 0 )
1245   {
1246     glBegin ( GL_TRIANGLES ) ;
1247
1248     for ( i = 0 ; i < TETR_NUM_FACES ; i++ )
1249     {
1250       glNormal3d ( -tet_r[i][0], -tet_r[i][1], -tet_r[i][2] ) ;
1251       for ( j = 0; j < 3; j++ )
1252       {
1253         double x = offset[0] + scale * tet_r[tet_i[i][j]][0] ;
1254         double y = offset[1] + scale * tet_r[tet_i[i][j]][1] ;
1255         double z = offset[2] + scale * tet_r[tet_i[i][j]][2] ;
1256         glVertex3d ( x, y, z ) ;
1257       }
1258     }
1259
1260     glEnd () ;
1261   }
1262   else if ( num_levels > 0 )
1263   {
1264     GLdouble local_offset[3] ;  /* Use a local variable to avoid buildup of roundoff errors */
1265     num_levels -- ;
1266     scale /= 2.0 ;
1267     for ( i = 0 ; i < TETR_NUM_FACES ; i++ )
1268     {
1269       local_offset[0] = offset[0] + scale * tet_r[i][0] ;
1270       local_offset[1] = offset[1] + scale * tet_r[i][1] ;
1271       local_offset[2] = offset[2] + scale * tet_r[i][2] ;
1272       glutSolidSierpinskiSponge ( num_levels, local_offset, scale ) ;
1273     }
1274   }
1275 }
1276
1277
1278
1279 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1280
1281
1282 void FGAPIENTRY glutWireTetrahedron( void )
1283 {
1284     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTetrahedron" );
1285
1286     fghTetrahedron( TRUE );
1287 }
1288 void FGAPIENTRY glutSolidTetrahedron( void )
1289 {
1290     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTetrahedron" );
1291
1292     fghTetrahedron( FALSE );
1293 }
1294
1295
1296 /*** END OF FILE ***/