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