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