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