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