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