Remove left-over printf
[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(GLdouble *vertices, GLdouble *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_DOUBLE, 0, vertices);
53     glNormalPointer(GL_DOUBLE, 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(GLdouble *vertices, GLdouble *normals, GLubyte *vertIdxs, GLsizei numVertices, GLsizei numEdgePerFace)
63 {
64     glEnableClientState(GL_VERTEX_ARRAY);
65     glEnableClientState(GL_NORMAL_ARRAY);
66
67     glVertexPointer(3, GL_DOUBLE, 0, vertices);
68     glNormalPointer(GL_DOUBLE, 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, GLdouble *vertices, GLubyte *vertIndices, GLdouble *normals, GLdouble *vertOut, GLdouble *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, GLdouble *vertices, GLubyte *vertIndices, GLdouble *normals, GLdouble *vertOut, GLdouble *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 GLdouble name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
152     static GLdouble 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 GLdouble  name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
162     static GLdouble  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 GLdouble cube_v[CUBE_NUM_VERT*3] =
180 {
181      .5, .5, .5,
182     -.5, .5, .5,
183     -.5,-.5, .5,
184      .5,-.5, .5,
185      .5,-.5,-.5,
186      .5, .5,-.5,
187     -.5, .5,-.5,
188     -.5,-.5,-.5
189 };
190 /* Normal Vectors */
191 static GLdouble cube_n[CUBE_NUM_FACES*3] =
192 {
193      0.0, 0.0, 1.0,
194      1.0, 0.0, 0.0,
195      0.0, 1.0, 0.0,
196     -1.0, 0.0, 0.0,
197      0.0,-1.0, 0.0,
198      0.0, 0.0,-1.0
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 GLdouble dodecahedron_v[DODECAHEDRON_NUM_VERT*3] =
228 {
229      0.0          ,  1.61803398875,  0.61803398875,
230     -1.0          ,  1.0          ,  1.0          ,
231     -0.61803398875,  0.0          ,  1.61803398875,
232      0.61803398875,  0.0          ,  1.61803398875,
233      1.0          ,  1.0          ,  1.0          ,
234      0.0          ,  1.61803398875, -0.61803398875,
235      1.0          ,  1.0          , -1.0          ,
236      0.61803398875,  0.0          , -1.61803398875,
237     -0.61803398875,  0.0          , -1.61803398875,
238     -1.0          ,  1.0          , -1.0          ,
239      0.0          , -1.61803398875,  0.61803398875,
240      1.0          , -1.0          ,  1.0          ,
241     -1.0          , -1.0          ,  1.0          ,
242      0.0          , -1.61803398875, -0.61803398875,
243     -1.0          , -1.0          , -1.0          ,
244      1.0          , -1.0          , -1.0          ,
245      1.61803398875, -0.61803398875,  0.0          ,
246      1.61803398875,  0.61803398875,  0.0          ,
247     -1.61803398875,  0.61803398875,  0.0          ,
248     -1.61803398875, -0.61803398875,  0.0
249 };
250 /* Normal Vectors */
251 static GLdouble dodecahedron_n[DODECAHEDRON_NUM_FACES*3] =
252 {
253      0.0           ,  0.525731112119,  0.850650808354,
254      0.0           ,  0.525731112119, -0.850650808354,
255      0.0           , -0.525731112119,  0.850650808354,
256      0.0           , -0.525731112119, -0.850650808354,
257
258      0.850650808354, 0.0            ,  0.525731112119,
259     -0.850650808354, 0.0            ,  0.525731112119,
260      0.850650808354, 0.0            , -0.525731112119,
261     -0.850650808354, 0.0            , -0.525731112119,
262
263      0.525731112119,  0.850650808354, 0.0            ,
264      0.525731112119, -0.850650808354, 0.0            ,
265     -0.525731112119,  0.850650808354, 0.0            , 
266     -0.525731112119, -0.850650808354, 0.0            ,
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 GLdouble icosahedron_v[ICOSAHEDRON_NUM_VERT*3] =
299 {
300      1.0,             0.0,             0.0           ,
301      0.447213595500,  0.894427191000,  0.0           ,
302      0.447213595500,  0.276393202252,  0.850650808354,
303      0.447213595500, -0.723606797748,  0.525731112119,
304      0.447213595500, -0.723606797748, -0.525731112119,
305      0.447213595500,  0.276393202252, -0.850650808354,
306     -0.447213595500, -0.894427191000,  0.0           ,
307     -0.447213595500, -0.276393202252,  0.850650808354,
308     -0.447213595500,  0.723606797748,  0.525731112119,
309     -0.447213595500,  0.723606797748, -0.525731112119,
310     -0.447213595500, -0.276393202252, -0.850650808354,
311     -1.0,             0.0,             0.0           
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 GLdouble icosahedron_n[ICOSAHEDRON_NUM_FACES*3] =
319 {
320      0.760845213037948,  0.470228201835026,  0.341640786498800,
321      0.760845213036861, -0.179611190632978,  0.552786404500000,
322      0.760845213033849, -0.581234022404097,                  0,
323      0.760845213036861, -0.179611190632978, -0.552786404500000,
324      0.760845213037948,  0.470228201835026, -0.341640786498800,
325      0.179611190628666,  0.760845213037948,  0.552786404498399,
326      0.179611190634277, -0.290617011204044,  0.894427191000000,
327      0.179611190633958, -0.940456403667806,                  0,
328      0.179611190634278, -0.290617011204044, -0.894427191000000,
329      0.179611190628666,  0.760845213037948, -0.552786404498399,
330     -0.179611190633958,  0.940456403667806,                  0,
331     -0.179611190634277,  0.290617011204044,  0.894427191000000,
332     -0.179611190628666, -0.760845213037948,  0.552786404498399,
333     -0.179611190628666, -0.760845213037948, -0.552786404498399,
334     -0.179611190634277,  0.290617011204044, -0.894427191000000,
335     -0.760845213036861,  0.179611190632978, -0.552786404500000,
336     -0.760845213033849,  0.581234022404097,                  0,
337     -0.760845213036861,  0.179611190632978,  0.552786404500000,
338     -0.760845213037948, -0.470228201835026,  0.341640786498800,
339     -0.760845213037948, -0.470228201835026, -0.341640786498800,
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 GLdouble octahedron_v[OCTAHEDRON_NUM_VERT*3] =
378 {
379      1.,  0.,  0.,
380      0.,  1.,  0.,
381      0.,  0.,  1.,
382     -1.,  0.,  0.,
383      0., -1.,  0.,
384      0.,  0., -1.,
385
386 };
387 /* Normal Vectors */
388 static GLdouble octahedron_n[OCTAHEDRON_NUM_FACES*3] =
389 {
390      0.577350269189, 0.577350269189, 0.577350269189,    /* sqrt(1/3) */
391      0.577350269189, 0.577350269189,-0.577350269189,
392      0.577350269189,-0.577350269189, 0.577350269189,
393      0.577350269189,-0.577350269189,-0.577350269189,
394     -0.577350269189, 0.577350269189, 0.577350269189,
395     -0.577350269189, 0.577350269189,-0.577350269189,
396     -0.577350269189,-0.577350269189, 0.577350269189,
397     -0.577350269189,-0.577350269189,-0.577350269189
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 GLdouble rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] =
425 {
426      0.0,             0.0,             1.0,
427      0.707106781187,  0.0           ,  0.5,
428      0.0           ,  0.707106781187,  0.5,
429     -0.707106781187,  0.0           ,  0.5,
430      0.0           , -0.707106781187,  0.5,
431      0.707106781187,  0.707106781187,  0.0,
432     -0.707106781187,  0.707106781187,  0.0,
433     -0.707106781187, -0.707106781187,  0.0,
434      0.707106781187, -0.707106781187,  0.0,
435      0.707106781187,  0.0           , -0.5,
436      0.0           ,  0.707106781187, -0.5,
437     -0.707106781187,  0.0           , -0.5,
438      0.0           , -0.707106781187, -0.5,
439      0.0,             0.0,            -1.0
440 };
441 /* Normal Vectors */
442 static GLdouble rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] =
443 {
444      0.353553390594,  0.353553390594,  0.5,
445     -0.353553390594,  0.353553390594,  0.5,
446     -0.353553390594, -0.353553390594,  0.5,
447      0.353553390594, -0.353553390594,  0.5,
448      0.0           ,  1.0           ,  0.0,
449     -1.0           ,  0.0           ,  0.0,
450      0.0           , -1.0           ,  0.0,
451      1.0           ,  0.0           ,  0.0,
452      0.353553390594,  0.353553390594, -0.5,
453     -0.353553390594,  0.353553390594, -0.5,
454     -0.353553390594, -0.353553390594, -0.5,
455      0.353553390594, -0.353553390594, -0.5
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 GLdouble tetrahedron_v[TETRAHEDRON_NUM_VERT*3] =
495 {
496                 1.0,             0.0,             0.0,
497     -0.333333333333,  0.942809041582,             0.0,
498     -0.333333333333, -0.471404520791,  0.816496580928,
499     -0.333333333333, -0.471404520791, -0.816496580928
500 };
501 /* Normal Vectors */
502 static GLdouble tetrahedron_n[TETRAHEDRON_NUM_FACES*3] =
503 {
504     -           1.0,             0.0,             0.0,
505      0.333333333333, -0.942809041582,             0.0,
506      0.333333333333,  0.471404520791, -0.816496580928,
507      0.333333333333,  0.471404520791,  0.816496580928
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, GLdouble offset[3], GLdouble scale, GLdouble* vertices, GLdouble* 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  ] = offset[0] + scale * tetrahedron_v[vertIdx  ];
541                 vertices[outIdx+1] = offset[1] + scale * tetrahedron_v[vertIdx+1];
542                 vertices[outIdx+2] = 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         GLdouble 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(GLdouble **sint, GLdouble **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 GLdouble angle = (halfCircle?1:2)*M_PI/(GLdouble)( ( n == 0 ) ? 1 : n );
586
587     /* Allocate memory for n samples, plus duplicate of first entry at the end */
588     *sint = malloc(sizeof(GLdouble) * (size+1));
589     *cost = malloc(sizeof(GLdouble) * (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] = sin(angle*i);
606         (*cost)[i] = cos(angle*i);
607     }
608
609     
610     if (halfCircle)
611     {
612         (*sint)[size] =  0.0;   /* sin PI */
613         (*cost)[size] = -1.0;   /* 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( GLdouble dSize, GLboolean useWireMode )
649 {
650     GLdouble *vertices;
651
652     if (!cubeCached)
653     {
654         fghCubeGenerate();
655         cubeCached = GL_TRUE;
656     }
657
658     if (dSize!=1.)
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(GLdouble));
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.)
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, GLdouble offset[3], GLdouble scale, GLboolean useWireMode )
695 {
696     GLdouble *vertices;
697     GLdouble * 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(GLdouble));
706         normals  = malloc(numVert*3 * sizeof(GLdouble));
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(GLdouble radius, GLint slices, GLint stacks)
737 {
738     int i,j;
739
740     /* Adjust z and radius as stacks are drawn. */
741
742     GLdouble z0,z1;
743     GLdouble r0,r1;
744
745     /* Pre-computed circle */
746
747     GLdouble *sint1,*cost1;
748     GLdouble *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.0;
758     z1 = cost2[(stacks>0)?1:0];
759     r0 = 0.0;
760     r1 = sint2[(stacks>0)?1:0];
761
762     glBegin(GL_TRIANGLE_FAN);
763
764         glNormal3d(0,0,1);
765         glVertex3d(0,0,radius);
766
767         for (j=slices; j>=0; j--)
768         {
769             glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
770             glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
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*radius, sint1[j]*r1*radius, z1*radius);
788                 glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
789                 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
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*radius, sint1[j]*r0*radius, z0*radius);
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(GLdouble radius, GLint slices, GLint stacks)
825 {
826     int i,j;
827
828     /* Adjust z and radius as stacks and slices are drawn. */
829
830     GLdouble r;
831     GLdouble x,y,z;
832
833     /* Pre-computed circle */
834
835     GLdouble *sint1,*cost1;
836     GLdouble *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                 glNormal3d(x,y,z);
858                 glVertex3d(x*r*radius,y*r*radius,z*radius);
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                 glNormal3d(x,y,z);
877                 glVertex3d(x*radius,y*radius,z*radius);
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( GLdouble base, GLdouble height, GLint slices, GLint stacks )
895 {
896     int i,j;
897
898     /* Step in z and radius as stacks are drawn. */
899
900     GLdouble z0,z1;
901     GLdouble r0,r1;
902
903     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
904     const GLdouble rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
905
906     /* Scaling factors for vertex normals */
907
908     const GLdouble cosn = ( height / sqrt ( height * height + base * base ));
909     const GLdouble sinn = ( base   / sqrt ( height * height + base * base ));
910
911     /* Pre-computed circle */
912
913     GLdouble *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.0;
922     z1 = zStep;
923
924     r0 = base;
925     r1 = r0 - rStep;
926
927     glBegin(GL_TRIANGLE_FAN);
928
929         glNormal3d(0.0,0.0,-1.0);
930         glVertex3d(0.0,0.0, z0 );
931
932         for (j=0; j<=slices; j++)
933             glVertex3d(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                 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
946                 glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
947                 glVertex3d(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         glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
961
962         for (j=0; j<slices; j++)
963         {
964             glVertex3d(cost[j+0]*r0,   sint[j+0]*r0,   z0    );
965             glVertex3d(0,              0,              height);
966             glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn  );
967             glVertex3d(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( GLdouble base, GLdouble height, GLint slices, GLint stacks)
982 {
983     int i,j;
984
985     /* Step in z and radius as stacks are drawn. */
986
987     GLdouble z = 0.0;
988     GLdouble r = base;
989
990     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
991     const GLdouble rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
992
993     /* Scaling factors for vertex normals */
994
995     const GLdouble cosn = ( height / sqrt ( height * height + base * base ));
996     const GLdouble sinn = ( base   / sqrt ( height * height + base * base ));
997
998     /* Pre-computed circle */
999
1000     GLdouble *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                 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
1015                 glVertex3d(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 = base;
1027
1028     glBegin(GL_LINES);
1029
1030         for (j=0; j<slices; j++)
1031         {
1032             glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn  );
1033             glVertex3d(cost[j]*r,    sint[j]*r,    0.0   );
1034             glVertex3d(0.0,          0.0,          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(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1050 {
1051     int i,j;
1052
1053     /* Step in z and radius as stacks are drawn. */
1054
1055     GLdouble z0,z1;
1056     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1057
1058     /* Pre-computed circle */
1059
1060     GLdouble *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         glNormal3d(0.0, 0.0, -1.0 );
1070         glVertex3d(0.0, 0.0,  0.0 );
1071         for (j=0; j<=slices; j++)
1072           glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
1073     glEnd();
1074
1075     glBegin(GL_TRIANGLE_FAN);
1076         glNormal3d(0.0, 0.0, 1.0   );
1077         glVertex3d(0.0, 0.0, height);
1078         for (j=slices; j>=0; j--)
1079           glVertex3d(cost[j]*radius, sint[j]*radius, height);
1080     glEnd();
1081
1082     /* Do the stacks */
1083
1084     z0 = 0.0;
1085     z1 = zStep;
1086
1087     for (i=1; i<=stacks; i++)
1088     {
1089         if (i==stacks)
1090             z1 = height;
1091
1092         glBegin(GL_QUAD_STRIP);
1093             for (j=0; j<=slices; j++ )
1094             {
1095                 glNormal3d(cost[j],        sint[j],        0.0 );
1096                 glVertex3d(cost[j]*radius, sint[j]*radius, z0  );
1097                 glVertex3d(cost[j]*radius, sint[j]*radius, 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(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1114 {
1115     int i,j;
1116
1117     /* Step in z and radius as stacks are drawn. */
1118
1119           GLdouble z = 0.0;
1120     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1121
1122     /* Pre-computed circle */
1123
1124     GLdouble *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 = height;
1136
1137         glBegin(GL_LINE_LOOP);
1138
1139             for( j=0; j<slices; j++ )
1140             {
1141                 glNormal3d(cost[j],        sint[j],        0.0);
1142                 glVertex3d(cost[j]*radius, sint[j]*radius, 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             glNormal3d(cost[j],        sint[j],        0.0   );
1157             glVertex3d(cost[j]*radius, sint[j]*radius, 0.0   );
1158             glVertex3d(cost[j]*radius, sint[j]*radius, 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( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1173 {
1174   GLdouble  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1175   GLdouble *vertex, *normal;
1176   int    i, j;
1177   GLdouble spsi, cpsi, sphi, cphi ;
1178
1179   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1180
1181   if ( nSides < 1 ) nSides = 1;
1182   if ( nRings < 1 ) nRings = 1;
1183
1184   /* Allocate the vertices array */
1185   vertex = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1186   normal = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1187
1188   glPushMatrix();
1189
1190   dpsi =  2.0 * M_PI / (GLdouble)nRings ;
1191   dphi = -2.0 * M_PI / (GLdouble)nSides ;
1192   psi  = 0.0;
1193
1194   for( j=0; j<nRings; j++ )
1195   {
1196     cpsi = cos ( psi ) ;
1197     spsi = sin ( psi ) ;
1198     phi = 0.0;
1199
1200     for( i=0; i<nSides; i++ )
1201     {
1202       int offset = 3 * ( j * nSides + i ) ;
1203       cphi = cos ( phi ) ;
1204       sphi = sin ( phi ) ;
1205       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1206       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1207       *(vertex + offset + 2) =                    sphi * iradius  ;
1208       *(normal + offset + 0) = cpsi * cphi ;
1209       *(normal + offset + 1) = spsi * cphi ;
1210       *(normal + offset + 2) =        sphi ;
1211       phi += dphi;
1212     }
1213
1214     psi += dpsi;
1215   }
1216
1217   for( i=0; i<nSides; i++ )
1218   {
1219     glBegin( GL_LINE_LOOP );
1220
1221     for( j=0; j<nRings; j++ )
1222     {
1223       int offset = 3 * ( j * nSides + i ) ;
1224       glNormal3dv( normal + offset );
1225       glVertex3dv( vertex + offset );
1226     }
1227
1228     glEnd();
1229   }
1230
1231   for( j=0; j<nRings; j++ )
1232   {
1233     glBegin(GL_LINE_LOOP);
1234
1235     for( i=0; i<nSides; i++ )
1236     {
1237       int offset = 3 * ( j * nSides + i ) ;
1238       glNormal3dv( normal + offset );
1239       glVertex3dv( vertex + offset );
1240     }
1241
1242     glEnd();
1243   }
1244
1245   free ( vertex ) ;
1246   free ( normal ) ;
1247   glPopMatrix();
1248 }
1249
1250 /*
1251  * Draws a solid torus
1252  */
1253 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1254 {
1255   GLdouble  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1256   GLdouble *vertex, *normal;
1257   int    i, j;
1258   GLdouble spsi, cpsi, sphi, cphi ;
1259
1260   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1261
1262   if ( nSides < 1 ) nSides = 1;
1263   if ( nRings < 1 ) nRings = 1;
1264
1265   /* Increment the number of sides and rings to allow for one more point than surface */
1266   nSides ++ ;
1267   nRings ++ ;
1268
1269   /* Allocate the vertices array */
1270   vertex = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1271   normal = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1272
1273   glPushMatrix();
1274
1275   dpsi =  2.0 * M_PI / (GLdouble)(nRings - 1) ;
1276   dphi = -2.0 * M_PI / (GLdouble)(nSides - 1) ;
1277   psi  = 0.0;
1278
1279   for( j=0; j<nRings; j++ )
1280   {
1281     cpsi = cos ( psi ) ;
1282     spsi = sin ( psi ) ;
1283     phi = 0.0;
1284
1285     for( i=0; i<nSides; i++ )
1286     {
1287       int offset = 3 * ( j * nSides + i ) ;
1288       cphi = cos ( phi ) ;
1289       sphi = sin ( phi ) ;
1290       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1291       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1292       *(vertex + offset + 2) =                    sphi * iradius  ;
1293       *(normal + offset + 0) = cpsi * cphi ;
1294       *(normal + offset + 1) = spsi * cphi ;
1295       *(normal + offset + 2) =        sphi ;
1296       phi += dphi;
1297     }
1298
1299     psi += dpsi;
1300   }
1301
1302     glBegin( GL_QUADS );
1303   for( i=0; i<nSides-1; i++ )
1304   {
1305     for( j=0; j<nRings-1; j++ )
1306     {
1307       int offset = 3 * ( j * nSides + i ) ;
1308       glNormal3dv( normal + offset );
1309       glVertex3dv( vertex + offset );
1310       glNormal3dv( normal + offset + 3 );
1311       glVertex3dv( vertex + offset + 3 );
1312       glNormal3dv( normal + offset + 3 * nSides + 3 );
1313       glVertex3dv( vertex + offset + 3 * nSides + 3 );
1314       glNormal3dv( normal + offset + 3 * nSides );
1315       glVertex3dv( vertex + offset + 3 * nSides );
1316     }
1317   }
1318
1319   glEnd();
1320
1321   free ( vertex ) ;
1322   free ( normal ) ;
1323   glPopMatrix();
1324 }
1325
1326
1327
1328 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1329 /* Macro to generate interface functions */
1330 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1331     void FGAPIENTRY glutWire##nameICaps( void )\
1332     {\
1333         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1334         fgh##nameICaps( TRUE );\
1335     }\
1336     void FGAPIENTRY glutSolid##nameICaps( void )\
1337     {\
1338         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1339         fgh##nameICaps( FALSE );\
1340     }
1341
1342 void FGAPIENTRY glutWireCube( GLdouble dSize )
1343 {
1344     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1345     fghCube( dSize, TRUE );
1346 }
1347 void FGAPIENTRY glutSolidCube( GLdouble dSize )
1348 {
1349     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1350     fghCube( dSize, FALSE );
1351 }
1352
1353 DECLARE_SHAPE_INTERFACE(Dodecahedron);
1354 DECLARE_SHAPE_INTERFACE(Icosahedron);
1355 DECLARE_SHAPE_INTERFACE(Octahedron);
1356 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron);
1357
1358 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1359 {
1360     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1361     fghSierpinskiSponge ( num_levels, offset, scale, TRUE );
1362 }
1363 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1364 {
1365     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1366     fghSierpinskiSponge ( num_levels, offset, scale, FALSE );
1367 }
1368
1369 DECLARE_SHAPE_INTERFACE(Tetrahedron);
1370
1371
1372 /*** END OF FILE ***/