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