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