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