6146ea4bbc40c0689e2b78dc0e9b8abea430b241
[freeglut] / 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     else
709         fghDrawGeometry(GL_TRIANGLES,cube_verts,cube_norms,cube_edgeFlags,CUBE_VERT_PER_OBJ_TRI,useWireMode);
710 }
711
712 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON);
713 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON);
714 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON);
715 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON);
716 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON);
717
718 static void fghSierpinskiSponge ( int numLevels, GLdouble offset[3], GLdouble scale, GLboolean useWireMode )
719 {
720     GLdouble *vertices;
721     GLdouble * normals;
722     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
723     GLsizei    numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
724
725     if (numTetr)
726     {
727         /* Allocate memory */
728         vertices = malloc(numVert*3 * sizeof(GLdouble));
729         normals  = malloc(numVert*3 * sizeof(GLdouble));
730         /* Bail out if memory allocation fails, fgError never returns */
731         if (!(*vertices) || !(*normals))
732         {
733             free(*vertices);
734             free(*normals);
735             fgError("Failed to allocate memory in fghSierpinskiSponge");
736         }
737
738         /* Generate elements */
739         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
740
741         /* Draw and cleanup */
742         fghDrawGeometry(GL_TRIANGLES,vertices,normals,NULL,numVert,useWireMode);
743         free(vertices);
744         free(normals );
745     }
746 }
747
748
749 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
750
751
752 /*
753  * Draws a solid sphere
754  */
755 void FGAPIENTRY glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
756 {
757     int i,j;
758
759     /* Adjust z and radius as stacks are drawn. */
760
761     double z0,z1;
762     double r0,r1;
763
764     /* Pre-computed circle */
765
766     double *sint1,*cost1;
767     double *sint2,*cost2;
768
769     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
770
771     fghCircleTable(&sint1,&cost1,-slices);
772     fghCircleTable(&sint2,&cost2,stacks*2);
773
774     /* The top stack is covered with a triangle fan */
775
776     z0 = 1.0;
777     z1 = cost2[(stacks>0)?1:0];
778     r0 = 0.0;
779     r1 = sint2[(stacks>0)?1:0];
780
781     glBegin(GL_TRIANGLE_FAN);
782
783         glNormal3d(0,0,1);
784         glVertex3d(0,0,radius);
785
786         for (j=slices; j>=0; j--)
787         {
788             glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
789             glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
790         }
791
792     glEnd();
793
794     /* Cover each stack with a quad strip, except the top and bottom stacks */
795
796     for( i=1; i<stacks-1; i++ )
797     {
798         z0 = z1; z1 = cost2[i+1];
799         r0 = r1; r1 = sint2[i+1];
800
801         glBegin(GL_QUAD_STRIP);
802
803             for(j=0; j<=slices; j++)
804             {
805                 glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
806                 glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
807                 glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
808                 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
809             }
810
811         glEnd();
812     }
813
814     /* The bottom stack is covered with a triangle fan */
815
816     z0 = z1;
817     r0 = r1;
818
819     glBegin(GL_TRIANGLE_FAN);
820
821         glNormal3d(0,0,-1);
822         glVertex3d(0,0,-radius);
823
824         for (j=0; j<=slices; j++)
825         {
826             glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
827             glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
828         }
829
830     glEnd();
831
832     /* Release sin and cos tables */
833
834     free(sint1);
835     free(cost1);
836     free(sint2);
837     free(cost2);
838 }
839
840 /*
841  * Draws a wire sphere
842  */
843 void FGAPIENTRY glutWireSphere(GLdouble radius, GLint slices, GLint stacks)
844 {
845     int i,j;
846
847     /* Adjust z and radius as stacks and slices are drawn. */
848
849     double r;
850     double x,y,z;
851
852     /* Pre-computed circle */
853
854     double *sint1,*cost1;
855     double *sint2,*cost2;
856
857     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
858
859     fghCircleTable(&sint1,&cost1,-slices  );
860     fghCircleTable(&sint2,&cost2, stacks*2);
861
862     /* Draw a line loop for each stack */
863
864     for (i=1; i<stacks; i++)
865     {
866         z = cost2[i];
867         r = sint2[i];
868
869         glBegin(GL_LINE_LOOP);
870
871             for(j=0; j<=slices; j++)
872             {
873                 x = cost1[j];
874                 y = sint1[j];
875
876                 glNormal3d(x,y,z);
877                 glVertex3d(x*r*radius,y*r*radius,z*radius);
878             }
879
880         glEnd();
881     }
882
883     /* Draw a line loop for each slice */
884
885     for (i=0; i<slices; i++)
886     {
887         glBegin(GL_LINE_STRIP);
888
889             for(j=0; j<=stacks; j++)
890             {
891                 x = cost1[i]*sint2[j];
892                 y = sint1[i]*sint2[j];
893                 z = cost2[j];
894
895                 glNormal3d(x,y,z);
896                 glVertex3d(x*radius,y*radius,z*radius);
897             }
898
899         glEnd();
900     }
901
902     /* Release sin and cos tables */
903
904     free(sint1);
905     free(cost1);
906     free(sint2);
907     free(cost2);
908 }
909
910 /*
911  * Draws a solid cone
912  */
913 void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
914 {
915     int i,j;
916
917     /* Step in z and radius as stacks are drawn. */
918
919     double z0,z1;
920     double r0,r1;
921
922     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
923     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
924
925     /* Scaling factors for vertex normals */
926
927     const double cosn = ( height / sqrt ( height * height + base * base ));
928     const double sinn = ( base   / sqrt ( height * height + base * base ));
929
930     /* Pre-computed circle */
931
932     double *sint,*cost;
933
934     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
935
936     fghCircleTable(&sint,&cost,-slices);
937
938     /* Cover the circular base with a triangle fan... */
939
940     z0 = 0.0;
941     z1 = zStep;
942
943     r0 = base;
944     r1 = r0 - rStep;
945
946     glBegin(GL_TRIANGLE_FAN);
947
948         glNormal3d(0.0,0.0,-1.0);
949         glVertex3d(0.0,0.0, z0 );
950
951         for (j=0; j<=slices; j++)
952             glVertex3d(cost[j]*r0, sint[j]*r0, z0);
953
954     glEnd();
955
956     /* Cover each stack with a quad strip, except the top stack */
957
958     for( i=0; i<stacks-1; i++ )
959     {
960         glBegin(GL_QUAD_STRIP);
961
962             for(j=0; j<=slices; j++)
963             {
964                 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
965                 glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
966                 glVertex3d(cost[j]*r1,   sint[j]*r1,   z1  );
967             }
968
969             z0 = z1; z1 += zStep;
970             r0 = r1; r1 -= rStep;
971
972         glEnd();
973     }
974
975     /* The top stack is covered with individual triangles */
976
977     glBegin(GL_TRIANGLES);
978
979         glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
980
981         for (j=0; j<slices; j++)
982         {
983             glVertex3d(cost[j+0]*r0,   sint[j+0]*r0,   z0    );
984             glVertex3d(0,              0,              height);
985             glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn  );
986             glVertex3d(cost[j+1]*r0,   sint[j+1]*r0,   z0    );
987         }
988
989     glEnd();
990
991     /* Release sin and cos tables */
992
993     free(sint);
994     free(cost);
995 }
996
997 /*
998  * Draws a wire cone
999  */
1000 void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks)
1001 {
1002     int i,j;
1003
1004     /* Step in z and radius as stacks are drawn. */
1005
1006     double z = 0.0;
1007     double r = base;
1008
1009     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1010     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
1011
1012     /* Scaling factors for vertex normals */
1013
1014     const double cosn = ( height / sqrt ( height * height + base * base ));
1015     const double sinn = ( base   / sqrt ( height * height + base * base ));
1016
1017     /* Pre-computed circle */
1018
1019     double *sint,*cost;
1020
1021     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1022
1023     fghCircleTable(&sint,&cost,-slices);
1024
1025     /* Draw the stacks... */
1026
1027     for (i=0; i<stacks; i++)
1028     {
1029         glBegin(GL_LINE_LOOP);
1030
1031             for( j=0; j<slices; j++ )
1032             {
1033                 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
1034                 glVertex3d(cost[j]*r,    sint[j]*r,    z   );
1035             }
1036
1037         glEnd();
1038
1039         z += zStep;
1040         r -= rStep;
1041     }
1042
1043     /* Draw the slices */
1044
1045     r = base;
1046
1047     glBegin(GL_LINES);
1048
1049         for (j=0; j<slices; j++)
1050         {
1051             glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn  );
1052             glVertex3d(cost[j]*r,    sint[j]*r,    0.0   );
1053             glVertex3d(0.0,          0.0,          height);
1054         }
1055
1056     glEnd();
1057
1058     /* Release sin and cos tables */
1059
1060     free(sint);
1061     free(cost);
1062 }
1063
1064
1065 /*
1066  * Draws a solid cylinder
1067  */
1068 void FGAPIENTRY glutSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1069 {
1070     int i,j;
1071
1072     /* Step in z and radius as stacks are drawn. */
1073
1074     double z0,z1;
1075     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1076
1077     /* Pre-computed circle */
1078
1079     double *sint,*cost;
1080
1081     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1082
1083     fghCircleTable(&sint,&cost,-slices);
1084
1085     /* Cover the base and top */
1086
1087     glBegin(GL_TRIANGLE_FAN);
1088         glNormal3d(0.0, 0.0, -1.0 );
1089         glVertex3d(0.0, 0.0,  0.0 );
1090         for (j=0; j<=slices; j++)
1091           glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
1092     glEnd();
1093
1094     glBegin(GL_TRIANGLE_FAN);
1095         glNormal3d(0.0, 0.0, 1.0   );
1096         glVertex3d(0.0, 0.0, height);
1097         for (j=slices; j>=0; j--)
1098           glVertex3d(cost[j]*radius, sint[j]*radius, height);
1099     glEnd();
1100
1101     /* Do the stacks */
1102
1103     z0 = 0.0;
1104     z1 = zStep;
1105
1106     for (i=1; i<=stacks; i++)
1107     {
1108         if (i==stacks)
1109             z1 = height;
1110
1111         glBegin(GL_QUAD_STRIP);
1112             for (j=0; j<=slices; j++ )
1113             {
1114                 glNormal3d(cost[j],        sint[j],        0.0 );
1115                 glVertex3d(cost[j]*radius, sint[j]*radius, z0  );
1116                 glVertex3d(cost[j]*radius, sint[j]*radius, z1  );
1117             }
1118         glEnd();
1119
1120         z0 = z1; z1 += zStep;
1121     }
1122
1123     /* Release sin and cos tables */
1124
1125     free(sint);
1126     free(cost);
1127 }
1128
1129 /*
1130  * Draws a wire cylinder
1131  */
1132 void FGAPIENTRY glutWireCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1133 {
1134     int i,j;
1135
1136     /* Step in z and radius as stacks are drawn. */
1137
1138           double z = 0.0;
1139     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1140
1141     /* Pre-computed circle */
1142
1143     double *sint,*cost;
1144
1145     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1146
1147     fghCircleTable(&sint,&cost,-slices);
1148
1149     /* Draw the stacks... */
1150
1151     for (i=0; i<=stacks; i++)
1152     {
1153         if (i==stacks)
1154             z = height;
1155
1156         glBegin(GL_LINE_LOOP);
1157
1158             for( j=0; j<slices; j++ )
1159             {
1160                 glNormal3d(cost[j],        sint[j],        0.0);
1161                 glVertex3d(cost[j]*radius, sint[j]*radius, z  );
1162             }
1163
1164         glEnd();
1165
1166         z += zStep;
1167     }
1168
1169     /* Draw the slices */
1170
1171     glBegin(GL_LINES);
1172
1173         for (j=0; j<slices; j++)
1174         {
1175             glNormal3d(cost[j],        sint[j],        0.0   );
1176             glVertex3d(cost[j]*radius, sint[j]*radius, 0.0   );
1177             glVertex3d(cost[j]*radius, sint[j]*radius, height);
1178         }
1179
1180     glEnd();
1181
1182     /* Release sin and cos tables */
1183
1184     free(sint);
1185     free(cost);
1186 }
1187
1188 /*
1189  * Draws a wire torus
1190  */
1191 void FGAPIENTRY glutWireTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1192 {
1193   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1194   double *vertex, *normal;
1195   int    i, j;
1196   double spsi, cpsi, sphi, cphi ;
1197
1198   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1199
1200   if ( nSides < 1 ) nSides = 1;
1201   if ( nRings < 1 ) nRings = 1;
1202
1203   /* Allocate the vertices array */
1204   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1205   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1206
1207   glPushMatrix();
1208
1209   dpsi =  2.0 * M_PI / (double)nRings ;
1210   dphi = -2.0 * M_PI / (double)nSides ;
1211   psi  = 0.0;
1212
1213   for( j=0; j<nRings; j++ )
1214   {
1215     cpsi = cos ( psi ) ;
1216     spsi = sin ( psi ) ;
1217     phi = 0.0;
1218
1219     for( i=0; i<nSides; i++ )
1220     {
1221       int offset = 3 * ( j * nSides + i ) ;
1222       cphi = cos ( phi ) ;
1223       sphi = sin ( phi ) ;
1224       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1225       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1226       *(vertex + offset + 2) =                    sphi * iradius  ;
1227       *(normal + offset + 0) = cpsi * cphi ;
1228       *(normal + offset + 1) = spsi * cphi ;
1229       *(normal + offset + 2) =        sphi ;
1230       phi += dphi;
1231     }
1232
1233     psi += dpsi;
1234   }
1235
1236   for( i=0; i<nSides; i++ )
1237   {
1238     glBegin( GL_LINE_LOOP );
1239
1240     for( j=0; j<nRings; j++ )
1241     {
1242       int offset = 3 * ( j * nSides + i ) ;
1243       glNormal3dv( normal + offset );
1244       glVertex3dv( vertex + offset );
1245     }
1246
1247     glEnd();
1248   }
1249
1250   for( j=0; j<nRings; j++ )
1251   {
1252     glBegin(GL_LINE_LOOP);
1253
1254     for( i=0; i<nSides; i++ )
1255     {
1256       int offset = 3 * ( j * nSides + i ) ;
1257       glNormal3dv( normal + offset );
1258       glVertex3dv( vertex + offset );
1259     }
1260
1261     glEnd();
1262   }
1263
1264   free ( vertex ) ;
1265   free ( normal ) ;
1266   glPopMatrix();
1267 }
1268
1269 /*
1270  * Draws a solid torus
1271  */
1272 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1273 {
1274   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1275   double *vertex, *normal;
1276   int    i, j;
1277   double spsi, cpsi, sphi, cphi ;
1278
1279   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1280
1281   if ( nSides < 1 ) nSides = 1;
1282   if ( nRings < 1 ) nRings = 1;
1283
1284   /* Increment the number of sides and rings to allow for one more point than surface */
1285   nSides ++ ;
1286   nRings ++ ;
1287
1288   /* Allocate the vertices array */
1289   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1290   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1291
1292   glPushMatrix();
1293
1294   dpsi =  2.0 * M_PI / (double)(nRings - 1) ;
1295   dphi = -2.0 * M_PI / (double)(nSides - 1) ;
1296   psi  = 0.0;
1297
1298   for( j=0; j<nRings; j++ )
1299   {
1300     cpsi = cos ( psi ) ;
1301     spsi = sin ( psi ) ;
1302     phi = 0.0;
1303
1304     for( i=0; i<nSides; i++ )
1305     {
1306       int offset = 3 * ( j * nSides + i ) ;
1307       cphi = cos ( phi ) ;
1308       sphi = sin ( phi ) ;
1309       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1310       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1311       *(vertex + offset + 2) =                    sphi * iradius  ;
1312       *(normal + offset + 0) = cpsi * cphi ;
1313       *(normal + offset + 1) = spsi * cphi ;
1314       *(normal + offset + 2) =        sphi ;
1315       phi += dphi;
1316     }
1317
1318     psi += dpsi;
1319   }
1320
1321     glBegin( GL_QUADS );
1322   for( i=0; i<nSides-1; i++ )
1323   {
1324     for( j=0; j<nRings-1; j++ )
1325     {
1326       int offset = 3 * ( j * nSides + i ) ;
1327       glNormal3dv( normal + offset );
1328       glVertex3dv( vertex + offset );
1329       glNormal3dv( normal + offset + 3 );
1330       glVertex3dv( vertex + offset + 3 );
1331       glNormal3dv( normal + offset + 3 * nSides + 3 );
1332       glVertex3dv( vertex + offset + 3 * nSides + 3 );
1333       glNormal3dv( normal + offset + 3 * nSides );
1334       glVertex3dv( vertex + offset + 3 * nSides );
1335     }
1336   }
1337
1338   glEnd();
1339
1340   free ( vertex ) ;
1341   free ( normal ) ;
1342   glPopMatrix();
1343 }
1344
1345
1346
1347 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1348 /* Macro to generate interface functions */
1349 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1350     void FGAPIENTRY glutWire##nameICaps( void )\
1351     {\
1352         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1353         fgh##nameICaps( TRUE );\
1354     }\
1355     void FGAPIENTRY glutSolid##nameICaps( void )\
1356     {\
1357         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1358         fgh##nameICaps( FALSE );\
1359     }
1360
1361 void FGAPIENTRY glutWireCube( GLdouble dSize )
1362 {
1363     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1364     fghCube( dSize, TRUE );
1365 }
1366 void FGAPIENTRY glutSolidCube( GLdouble dSize )
1367 {
1368     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1369     fghCube( dSize, FALSE );
1370 }
1371
1372 DECLARE_SHAPE_INTERFACE(Dodecahedron);
1373 DECLARE_SHAPE_INTERFACE(Icosahedron);
1374 DECLARE_SHAPE_INTERFACE(Octahedron);
1375 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron);
1376
1377 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1378 {
1379     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1380     fghSierpinskiSponge ( num_levels, offset, scale, TRUE );
1381 }
1382 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1383 {
1384     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1385     fghSierpinskiSponge ( num_levels, offset, scale, FALSE );
1386 }
1387
1388 DECLARE_SHAPE_INTERFACE(Tetrahedron);
1389
1390
1391 /*** END OF FILE ***/