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