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