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