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