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