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