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