got one fo the cube's normals wrong
[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     { 0.0, 0.0, 1.0},
130     { 1.0, 0.0, 0.0},
131     { 0.0, 1.0, 0.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][3] =
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     {
332         fghCubeGenerate();
333         cubeCached = TRUE;
334     }
335
336     if (dSize!=1.)
337     {
338         /* Need to build new */
339         fghDrawGeometry(GL_QUADS,cube_verts,cube_norms,CUBE_VERT_PER_TETR,useWireMode);
340     }
341     else
342         fghDrawGeometry(GL_QUADS,cube_verts,cube_norms,CUBE_VERT_PER_TETR,useWireMode);
343 }
344
345 static void fghTetrahedron( GLboolean useWireMode )
346 {
347     if (!tetrCached)
348     {
349         fghTetrahedronGenerate();
350         tetrCached = TRUE;
351     }
352
353     fghDrawGeometry(GL_TRIANGLES,tetr_verts,tetr_norms,TETR_VERT_PER_TETR,useWireMode);
354 }
355
356 static void fghSierpinskiSponge ( int numLevels, GLdouble offset[3], GLdouble scale, GLboolean useWireMode )
357 {
358     double      *vertices;
359     double      * normals;
360     unsigned int  numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
361     unsigned int  numVert = numTetr*TETR_VERT_PER_TETR;
362
363     if (numTetr)
364     {
365         /* Allocate memory */
366         vertices = malloc(numVert*3 * sizeof(double));
367         normals  = malloc(numVert*3 * sizeof(double));
368
369         /* Generate elements */
370         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
371
372         /* Draw and cleanup */
373         fghDrawGeometry(GL_TRIANGLES,vertices,normals,numVert,useWireMode);
374         free(vertices);
375         free(normals );
376     }
377 }
378
379
380 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
381
382
383 /*
384  * Draws a solid sphere
385  */
386 void FGAPIENTRY glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
387 {
388     int i,j;
389
390     /* Adjust z and radius as stacks are drawn. */
391
392     double z0,z1;
393     double r0,r1;
394
395     /* Pre-computed circle */
396
397     double *sint1,*cost1;
398     double *sint2,*cost2;
399
400     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
401
402     fghCircleTable(&sint1,&cost1,-slices);
403     fghCircleTable(&sint2,&cost2,stacks*2);
404
405     /* The top stack is covered with a triangle fan */
406
407     z0 = 1.0;
408     z1 = cost2[(stacks>0)?1:0];
409     r0 = 0.0;
410     r1 = sint2[(stacks>0)?1:0];
411
412     glBegin(GL_TRIANGLE_FAN);
413
414         glNormal3d(0,0,1);
415         glVertex3d(0,0,radius);
416
417         for (j=slices; j>=0; j--)
418         {
419             glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
420             glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
421         }
422
423     glEnd();
424
425     /* Cover each stack with a quad strip, except the top and bottom stacks */
426
427     for( i=1; i<stacks-1; i++ )
428     {
429         z0 = z1; z1 = cost2[i+1];
430         r0 = r1; r1 = sint2[i+1];
431
432         glBegin(GL_QUAD_STRIP);
433
434             for(j=0; j<=slices; j++)
435             {
436                 glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
437                 glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
438                 glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
439                 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
440             }
441
442         glEnd();
443     }
444
445     /* The bottom stack is covered with a triangle fan */
446
447     z0 = z1;
448     r0 = r1;
449
450     glBegin(GL_TRIANGLE_FAN);
451
452         glNormal3d(0,0,-1);
453         glVertex3d(0,0,-radius);
454
455         for (j=0; j<=slices; j++)
456         {
457             glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
458             glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
459         }
460
461     glEnd();
462
463     /* Release sin and cos tables */
464
465     free(sint1);
466     free(cost1);
467     free(sint2);
468     free(cost2);
469 }
470
471 /*
472  * Draws a wire sphere
473  */
474 void FGAPIENTRY glutWireSphere(GLdouble radius, GLint slices, GLint stacks)
475 {
476     int i,j;
477
478     /* Adjust z and radius as stacks and slices are drawn. */
479
480     double r;
481     double x,y,z;
482
483     /* Pre-computed circle */
484
485     double *sint1,*cost1;
486     double *sint2,*cost2;
487
488     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
489
490     fghCircleTable(&sint1,&cost1,-slices  );
491     fghCircleTable(&sint2,&cost2, stacks*2);
492
493     /* Draw a line loop for each stack */
494
495     for (i=1; i<stacks; i++)
496     {
497         z = cost2[i];
498         r = sint2[i];
499
500         glBegin(GL_LINE_LOOP);
501
502             for(j=0; j<=slices; j++)
503             {
504                 x = cost1[j];
505                 y = sint1[j];
506
507                 glNormal3d(x,y,z);
508                 glVertex3d(x*r*radius,y*r*radius,z*radius);
509             }
510
511         glEnd();
512     }
513
514     /* Draw a line loop for each slice */
515
516     for (i=0; i<slices; i++)
517     {
518         glBegin(GL_LINE_STRIP);
519
520             for(j=0; j<=stacks; j++)
521             {
522                 x = cost1[i]*sint2[j];
523                 y = sint1[i]*sint2[j];
524                 z = cost2[j];
525
526                 glNormal3d(x,y,z);
527                 glVertex3d(x*radius,y*radius,z*radius);
528             }
529
530         glEnd();
531     }
532
533     /* Release sin and cos tables */
534
535     free(sint1);
536     free(cost1);
537     free(sint2);
538     free(cost2);
539 }
540
541 /*
542  * Draws a solid cone
543  */
544 void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
545 {
546     int i,j;
547
548     /* Step in z and radius as stacks are drawn. */
549
550     double z0,z1;
551     double r0,r1;
552
553     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
554     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
555
556     /* Scaling factors for vertex normals */
557
558     const double cosn = ( height / sqrt ( height * height + base * base ));
559     const double sinn = ( base   / sqrt ( height * height + base * base ));
560
561     /* Pre-computed circle */
562
563     double *sint,*cost;
564
565     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
566
567     fghCircleTable(&sint,&cost,-slices);
568
569     /* Cover the circular base with a triangle fan... */
570
571     z0 = 0.0;
572     z1 = zStep;
573
574     r0 = base;
575     r1 = r0 - rStep;
576
577     glBegin(GL_TRIANGLE_FAN);
578
579         glNormal3d(0.0,0.0,-1.0);
580         glVertex3d(0.0,0.0, z0 );
581
582         for (j=0; j<=slices; j++)
583             glVertex3d(cost[j]*r0, sint[j]*r0, z0);
584
585     glEnd();
586
587     /* Cover each stack with a quad strip, except the top stack */
588
589     for( i=0; i<stacks-1; i++ )
590     {
591         glBegin(GL_QUAD_STRIP);
592
593             for(j=0; j<=slices; j++)
594             {
595                 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
596                 glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
597                 glVertex3d(cost[j]*r1,   sint[j]*r1,   z1  );
598             }
599
600             z0 = z1; z1 += zStep;
601             r0 = r1; r1 -= rStep;
602
603         glEnd();
604     }
605
606     /* The top stack is covered with individual triangles */
607
608     glBegin(GL_TRIANGLES);
609
610         glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
611
612         for (j=0; j<slices; j++)
613         {
614             glVertex3d(cost[j+0]*r0,   sint[j+0]*r0,   z0    );
615             glVertex3d(0,              0,              height);
616             glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn  );
617             glVertex3d(cost[j+1]*r0,   sint[j+1]*r0,   z0    );
618         }
619
620     glEnd();
621
622     /* Release sin and cos tables */
623
624     free(sint);
625     free(cost);
626 }
627
628 /*
629  * Draws a wire cone
630  */
631 void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks)
632 {
633     int i,j;
634
635     /* Step in z and radius as stacks are drawn. */
636
637     double z = 0.0;
638     double r = base;
639
640     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
641     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
642
643     /* Scaling factors for vertex normals */
644
645     const double cosn = ( height / sqrt ( height * height + base * base ));
646     const double sinn = ( base   / sqrt ( height * height + base * base ));
647
648     /* Pre-computed circle */
649
650     double *sint,*cost;
651
652     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
653
654     fghCircleTable(&sint,&cost,-slices);
655
656     /* Draw the stacks... */
657
658     for (i=0; i<stacks; i++)
659     {
660         glBegin(GL_LINE_LOOP);
661
662             for( j=0; j<slices; j++ )
663             {
664                 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
665                 glVertex3d(cost[j]*r,    sint[j]*r,    z   );
666             }
667
668         glEnd();
669
670         z += zStep;
671         r -= rStep;
672     }
673
674     /* Draw the slices */
675
676     r = base;
677
678     glBegin(GL_LINES);
679
680         for (j=0; j<slices; j++)
681         {
682             glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn  );
683             glVertex3d(cost[j]*r,    sint[j]*r,    0.0   );
684             glVertex3d(0.0,          0.0,          height);
685         }
686
687     glEnd();
688
689     /* Release sin and cos tables */
690
691     free(sint);
692     free(cost);
693 }
694
695
696 /*
697  * Draws a solid cylinder
698  */
699 void FGAPIENTRY glutSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
700 {
701     int i,j;
702
703     /* Step in z and radius as stacks are drawn. */
704
705     double z0,z1;
706     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
707
708     /* Pre-computed circle */
709
710     double *sint,*cost;
711
712     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
713
714     fghCircleTable(&sint,&cost,-slices);
715
716     /* Cover the base and top */
717
718     glBegin(GL_TRIANGLE_FAN);
719         glNormal3d(0.0, 0.0, -1.0 );
720         glVertex3d(0.0, 0.0,  0.0 );
721         for (j=0; j<=slices; j++)
722           glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
723     glEnd();
724
725     glBegin(GL_TRIANGLE_FAN);
726         glNormal3d(0.0, 0.0, 1.0   );
727         glVertex3d(0.0, 0.0, height);
728         for (j=slices; j>=0; j--)
729           glVertex3d(cost[j]*radius, sint[j]*radius, height);
730     glEnd();
731
732     /* Do the stacks */
733
734     z0 = 0.0;
735     z1 = zStep;
736
737     for (i=1; i<=stacks; i++)
738     {
739         if (i==stacks)
740             z1 = height;
741
742         glBegin(GL_QUAD_STRIP);
743             for (j=0; j<=slices; j++ )
744             {
745                 glNormal3d(cost[j],        sint[j],        0.0 );
746                 glVertex3d(cost[j]*radius, sint[j]*radius, z0  );
747                 glVertex3d(cost[j]*radius, sint[j]*radius, z1  );
748             }
749         glEnd();
750
751         z0 = z1; z1 += zStep;
752     }
753
754     /* Release sin and cos tables */
755
756     free(sint);
757     free(cost);
758 }
759
760 /*
761  * Draws a wire cylinder
762  */
763 void FGAPIENTRY glutWireCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
764 {
765     int i,j;
766
767     /* Step in z and radius as stacks are drawn. */
768
769           double z = 0.0;
770     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
771
772     /* Pre-computed circle */
773
774     double *sint,*cost;
775
776     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
777
778     fghCircleTable(&sint,&cost,-slices);
779
780     /* Draw the stacks... */
781
782     for (i=0; i<=stacks; i++)
783     {
784         if (i==stacks)
785             z = height;
786
787         glBegin(GL_LINE_LOOP);
788
789             for( j=0; j<slices; j++ )
790             {
791                 glNormal3d(cost[j],        sint[j],        0.0);
792                 glVertex3d(cost[j]*radius, sint[j]*radius, z  );
793             }
794
795         glEnd();
796
797         z += zStep;
798     }
799
800     /* Draw the slices */
801
802     glBegin(GL_LINES);
803
804         for (j=0; j<slices; j++)
805         {
806             glNormal3d(cost[j],        sint[j],        0.0   );
807             glVertex3d(cost[j]*radius, sint[j]*radius, 0.0   );
808             glVertex3d(cost[j]*radius, sint[j]*radius, height);
809         }
810
811     glEnd();
812
813     /* Release sin and cos tables */
814
815     free(sint);
816     free(cost);
817 }
818
819 /*
820  * Draws a wire torus
821  */
822 void FGAPIENTRY glutWireTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
823 {
824   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
825   double *vertex, *normal;
826   int    i, j;
827   double spsi, cpsi, sphi, cphi ;
828
829   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
830
831   if ( nSides < 1 ) nSides = 1;
832   if ( nRings < 1 ) nRings = 1;
833
834   /* Allocate the vertices array */
835   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
836   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
837
838   glPushMatrix();
839
840   dpsi =  2.0 * M_PI / (double)nRings ;
841   dphi = -2.0 * M_PI / (double)nSides ;
842   psi  = 0.0;
843
844   for( j=0; j<nRings; j++ )
845   {
846     cpsi = cos ( psi ) ;
847     spsi = sin ( psi ) ;
848     phi = 0.0;
849
850     for( i=0; i<nSides; i++ )
851     {
852       int offset = 3 * ( j * nSides + i ) ;
853       cphi = cos ( phi ) ;
854       sphi = sin ( phi ) ;
855       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
856       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
857       *(vertex + offset + 2) =                    sphi * iradius  ;
858       *(normal + offset + 0) = cpsi * cphi ;
859       *(normal + offset + 1) = spsi * cphi ;
860       *(normal + offset + 2) =        sphi ;
861       phi += dphi;
862     }
863
864     psi += dpsi;
865   }
866
867   for( i=0; i<nSides; i++ )
868   {
869     glBegin( GL_LINE_LOOP );
870
871     for( j=0; j<nRings; j++ )
872     {
873       int offset = 3 * ( j * nSides + i ) ;
874       glNormal3dv( normal + offset );
875       glVertex3dv( vertex + offset );
876     }
877
878     glEnd();
879   }
880
881   for( j=0; j<nRings; j++ )
882   {
883     glBegin(GL_LINE_LOOP);
884
885     for( i=0; i<nSides; i++ )
886     {
887       int offset = 3 * ( j * nSides + i ) ;
888       glNormal3dv( normal + offset );
889       glVertex3dv( vertex + offset );
890     }
891
892     glEnd();
893   }
894
895   free ( vertex ) ;
896   free ( normal ) ;
897   glPopMatrix();
898 }
899
900 /*
901  * Draws a solid torus
902  */
903 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
904 {
905   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
906   double *vertex, *normal;
907   int    i, j;
908   double spsi, cpsi, sphi, cphi ;
909
910   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
911
912   if ( nSides < 1 ) nSides = 1;
913   if ( nRings < 1 ) nRings = 1;
914
915   /* Increment the number of sides and rings to allow for one more point than surface */
916   nSides ++ ;
917   nRings ++ ;
918
919   /* Allocate the vertices array */
920   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
921   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
922
923   glPushMatrix();
924
925   dpsi =  2.0 * M_PI / (double)(nRings - 1) ;
926   dphi = -2.0 * M_PI / (double)(nSides - 1) ;
927   psi  = 0.0;
928
929   for( j=0; j<nRings; j++ )
930   {
931     cpsi = cos ( psi ) ;
932     spsi = sin ( psi ) ;
933     phi = 0.0;
934
935     for( i=0; i<nSides; i++ )
936     {
937       int offset = 3 * ( j * nSides + i ) ;
938       cphi = cos ( phi ) ;
939       sphi = sin ( phi ) ;
940       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
941       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
942       *(vertex + offset + 2) =                    sphi * iradius  ;
943       *(normal + offset + 0) = cpsi * cphi ;
944       *(normal + offset + 1) = spsi * cphi ;
945       *(normal + offset + 2) =        sphi ;
946       phi += dphi;
947     }
948
949     psi += dpsi;
950   }
951
952     glBegin( GL_QUADS );
953   for( i=0; i<nSides-1; i++ )
954   {
955     for( j=0; j<nRings-1; j++ )
956     {
957       int offset = 3 * ( j * nSides + i ) ;
958       glNormal3dv( normal + offset );
959       glVertex3dv( vertex + offset );
960       glNormal3dv( normal + offset + 3 );
961       glVertex3dv( vertex + offset + 3 );
962       glNormal3dv( normal + offset + 3 * nSides + 3 );
963       glVertex3dv( vertex + offset + 3 * nSides + 3 );
964       glNormal3dv( normal + offset + 3 * nSides );
965       glVertex3dv( vertex + offset + 3 * nSides );
966     }
967   }
968
969   glEnd();
970
971   free ( vertex ) ;
972   free ( normal ) ;
973   glPopMatrix();
974 }
975
976 /*
977  *
978  */
979 void FGAPIENTRY glutWireDodecahedron( void )
980 {
981   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireDodecahedron" );
982
983   /* Magic Numbers:  It is possible to create a dodecahedron by attaching two pentagons to each face of
984    * of a cube.  The coordinates of the points are:
985    *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
986    * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2  or
987    *       x = 0.61803398875 and z = 1.61803398875.
988    */
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   glBegin ( GL_LINE_LOOP ) ;
996   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 ) ;
997   glEnd () ;
998   glBegin ( GL_LINE_LOOP ) ;
999   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 ) ;
1000   glEnd () ;
1001
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   glBegin ( GL_LINE_LOOP ) ;
1009   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 ) ;
1010   glEnd () ;
1011   glBegin ( GL_LINE_LOOP ) ;
1012   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 ) ;
1013   glEnd () ;
1014
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   glBegin ( GL_LINE_LOOP ) ;
1022   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 ) ;
1023   glEnd () ;
1024   glBegin ( GL_LINE_LOOP ) ;
1025   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 ) ;
1026   glEnd () ;
1027 }
1028
1029 /*
1030  *
1031  */
1032 void FGAPIENTRY glutSolidDodecahedron( void )
1033 {
1034   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidDodecahedron" );
1035
1036   /* Magic Numbers:  It is possible to create a dodecahedron by attaching two pentagons to each face of
1037    * of a cube.  The coordinates of the points are:
1038    *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
1039    * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
1040    *       x = 0.61803398875 and z = 1.61803398875.
1041    */
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   glBegin ( GL_POLYGON ) ;
1049   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 ) ;
1050   glEnd () ;
1051   glBegin ( GL_POLYGON ) ;
1052   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 ) ;
1053   glEnd () ;
1054
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   glBegin ( GL_POLYGON ) ;
1062   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 ) ;
1063   glEnd () ;
1064   glBegin ( GL_POLYGON ) ;
1065   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 ) ;
1066   glEnd () ;
1067
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   glBegin ( GL_POLYGON ) ;
1075   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 ) ;
1076   glEnd () ;
1077   glBegin ( GL_POLYGON ) ;
1078   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 ) ;
1079   glEnd () ;
1080 }
1081
1082 /*
1083  *
1084  */
1085 void FGAPIENTRY glutWireOctahedron( void )
1086 {
1087   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireOctahedron" );
1088
1089 #define RADIUS    1.0f
1090   glBegin( GL_LINE_LOOP );
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     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 );
1094     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 );
1095     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 );
1096     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 );
1097     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 );
1098     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 );
1099   glEnd();
1100 #undef RADIUS
1101 }
1102
1103 /*
1104  *
1105  */
1106 void FGAPIENTRY glutSolidOctahedron( void )
1107 {
1108   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidOctahedron" );
1109
1110 #define RADIUS    1.0f
1111   glBegin( GL_TRIANGLES );
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     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 );
1115     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 );
1116     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 );
1117     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 );
1118     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 );
1119     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 );
1120   glEnd();
1121 #undef RADIUS
1122 }
1123
1124 /*
1125  *
1126  */
1127 static double icos_r[12][3] = {
1128     {  1.0,             0.0,             0.0            },
1129     {  0.447213595500,  0.894427191000,  0.0            },
1130     {  0.447213595500,  0.276393202252,  0.850650808354 },
1131     {  0.447213595500, -0.723606797748,  0.525731112119 },
1132     {  0.447213595500, -0.723606797748, -0.525731112119 },
1133     {  0.447213595500,  0.276393202252, -0.850650808354 },
1134     { -0.447213595500, -0.894427191000,  0.0 },
1135     { -0.447213595500, -0.276393202252,  0.850650808354 },
1136     { -0.447213595500,  0.723606797748,  0.525731112119 },
1137     { -0.447213595500,  0.723606797748, -0.525731112119 },
1138     { -0.447213595500, -0.276393202252, -0.850650808354 },
1139     { -1.0,             0.0,             0.0            }
1140 };
1141
1142 static int icos_v [20][3] = {
1143     {  0,  1,  2 },
1144     {  0,  2,  3 },
1145     {  0,  3,  4 },
1146     {  0,  4,  5 },
1147     {  0,  5,  1 },
1148     {  1,  8,  2 },
1149     {  2,  7,  3 },
1150     {  3,  6,  4 },
1151     {  4, 10,  5 },
1152     {  5,  9,  1 },
1153     {  1,  9,  8 },
1154     {  2,  8,  7 },
1155     {  3,  7,  6 },
1156     {  4,  6, 10 },
1157     {  5, 10,  9 },
1158     { 11,  9, 10 },
1159     { 11,  8,  9 },
1160     { 11,  7,  8 },
1161     { 11,  6,  7 },
1162     { 11, 10,  6 }
1163 };
1164
1165 void FGAPIENTRY glutWireIcosahedron( void )
1166 {
1167   int i ;
1168
1169   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireIcosahedron" );
1170
1171   for ( i = 0; i < 20; i++ )
1172   {
1173     double normal[3] ;
1174     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] ) ;
1175     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] ) ;
1176     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] ) ;
1177     glBegin ( GL_LINE_LOOP ) ;
1178       glNormal3dv ( normal ) ;
1179       glVertex3dv ( icos_r[icos_v[i][0]] ) ;
1180       glVertex3dv ( icos_r[icos_v[i][1]] ) ;
1181       glVertex3dv ( icos_r[icos_v[i][2]] ) ;
1182     glEnd () ;
1183   }
1184 }
1185
1186 /*
1187  *
1188  */
1189 void FGAPIENTRY glutSolidIcosahedron( void )
1190 {
1191   int i ;
1192
1193   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidIcosahedron" );
1194
1195   glBegin ( GL_TRIANGLES ) ;
1196   for ( i = 0; i < 20; i++ )
1197   {
1198     double normal[3] ;
1199     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] ) ;
1200     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] ) ;
1201     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] ) ;
1202       glNormal3dv ( normal ) ;
1203       glVertex3dv ( icos_r[icos_v[i][0]] ) ;
1204       glVertex3dv ( icos_r[icos_v[i][1]] ) ;
1205       glVertex3dv ( icos_r[icos_v[i][2]] ) ;
1206   }
1207
1208   glEnd () ;
1209 }
1210
1211 /*
1212  *
1213  */
1214 static double rdod_r[14][3] = {
1215     {  0.0,             0.0,             1.0 },
1216     {  0.707106781187,  0.000000000000,  0.5 },
1217     {  0.000000000000,  0.707106781187,  0.5 },
1218     { -0.707106781187,  0.000000000000,  0.5 },
1219     {  0.000000000000, -0.707106781187,  0.5 },
1220     {  0.707106781187,  0.707106781187,  0.0 },
1221     { -0.707106781187,  0.707106781187,  0.0 },
1222     { -0.707106781187, -0.707106781187,  0.0 },
1223     {  0.707106781187, -0.707106781187,  0.0 },
1224     {  0.707106781187,  0.000000000000, -0.5 },
1225     {  0.000000000000,  0.707106781187, -0.5 },
1226     { -0.707106781187,  0.000000000000, -0.5 },
1227     {  0.000000000000, -0.707106781187, -0.5 },
1228     {  0.0,             0.0,            -1.0 }
1229 } ;
1230
1231 static int rdod_v [12][4] = {
1232     { 0,  1,  5,  2 },
1233     { 0,  2,  6,  3 },
1234     { 0,  3,  7,  4 },
1235     { 0,  4,  8,  1 },
1236     { 5, 10,  6,  2 },
1237     { 6, 11,  7,  3 },
1238     { 7, 12,  8,  4 },
1239     { 8,  9,  5,  1 },
1240     { 5,  9, 13, 10 },
1241     { 6, 10, 13, 11 },
1242     { 7, 11, 13, 12 },
1243     { 8, 12, 13,  9 }
1244 };
1245
1246 static double rdod_n[12][3] = {
1247     {  0.353553390594,  0.353553390594,  0.5 },
1248     { -0.353553390594,  0.353553390594,  0.5 },
1249     { -0.353553390594, -0.353553390594,  0.5 },
1250     {  0.353553390594, -0.353553390594,  0.5 },
1251     {  0.000000000000,  1.000000000000,  0.0 },
1252     { -1.000000000000,  0.000000000000,  0.0 },
1253     {  0.000000000000, -1.000000000000,  0.0 },
1254     {  1.000000000000,  0.000000000000,  0.0 },
1255     {  0.353553390594,  0.353553390594, -0.5 },
1256     { -0.353553390594,  0.353553390594, -0.5 },
1257     { -0.353553390594, -0.353553390594, -0.5 },
1258     {  0.353553390594, -0.353553390594, -0.5 }
1259 };
1260
1261 void FGAPIENTRY glutWireRhombicDodecahedron( void )
1262 {
1263   int i ;
1264
1265   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireRhombicDodecahedron" );
1266
1267   for ( i = 0; i < 12; i++ )
1268   {
1269     glBegin ( GL_LINE_LOOP ) ;
1270       glNormal3dv ( rdod_n[i] ) ;
1271       glVertex3dv ( rdod_r[rdod_v[i][0]] ) ;
1272       glVertex3dv ( rdod_r[rdod_v[i][1]] ) ;
1273       glVertex3dv ( rdod_r[rdod_v[i][2]] ) ;
1274       glVertex3dv ( rdod_r[rdod_v[i][3]] ) ;
1275     glEnd () ;
1276   }
1277 }
1278
1279 /*
1280  *
1281  */
1282 void FGAPIENTRY glutSolidRhombicDodecahedron( void )
1283 {
1284   int i ;
1285
1286   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidRhombicDodecahedron" );
1287
1288   glBegin ( GL_QUADS ) ;
1289   for ( i = 0; i < 12; i++ )
1290   {
1291       glNormal3dv ( rdod_n[i] ) ;
1292       glVertex3dv ( rdod_r[rdod_v[i][0]] ) ;
1293       glVertex3dv ( rdod_r[rdod_v[i][1]] ) ;
1294       glVertex3dv ( rdod_r[rdod_v[i][2]] ) ;
1295       glVertex3dv ( rdod_r[rdod_v[i][3]] ) ;
1296   }
1297
1298   glEnd () ;
1299 }
1300
1301
1302
1303 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1304 /*
1305  * Draws a wireframed cube.
1306  */
1307 void FGAPIENTRY glutWireCube( GLdouble dSize )
1308 {
1309     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1310     fghCube( dSize, TRUE );
1311 }
1312 void FGAPIENTRY glutSolidCube( GLdouble dSize )
1313 {
1314     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1315     fghCube( dSize, FALSE );
1316 }
1317
1318 void FGAPIENTRY glutWireTetrahedron( void )
1319 {
1320     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTetrahedron" );
1321     fghTetrahedron( TRUE );
1322 }
1323 void FGAPIENTRY glutSolidTetrahedron( void )
1324 {
1325     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTetrahedron" );
1326     fghTetrahedron( FALSE );
1327 }
1328
1329 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1330 {
1331     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1332     fghSierpinskiSponge ( num_levels, offset, scale, TRUE );
1333 }
1334 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1335 {
1336     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1337     fghSierpinskiSponge ( num_levels, offset, scale, FALSE );
1338 }
1339
1340
1341 /*** END OF FILE ***/