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