got rid of edgeflags as I now draw all wire frames face-by-face using
[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  *
570  * Notes:
571  *    It is the responsibility of the caller to free these tables
572  *    The size of the table is (n+1) to form a connected loop
573  *    The last entry is exactly the same as the first
574  *    The sign of n can be flipped to get the reverse loop
575  */
576 static void fghCircleTable(double **sint,double **cost,const int n)
577 {
578     int i;
579
580     /* Table size, the sign of n flips the circle direction */
581
582     const int size = abs(n);
583
584     /* Determine the angle between samples */
585
586     const double angle = 2*M_PI/(double)( ( n == 0 ) ? 1 : n );
587
588     /* Allocate memory for n samples, plus duplicate of first entry at the end */
589
590     *sint = (double *) calloc(sizeof(double), size+1);
591     *cost = (double *) calloc(sizeof(double), size+1);
592
593     /* Bail out if memory allocation fails, fgError never returns */
594
595     if (!(*sint) || !(*cost))
596     {
597         free(*sint);
598         free(*cost);
599         fgError("Failed to allocate memory in fghCircleTable");
600     }
601
602     /* Compute cos and sin around the circle */
603
604     (*sint)[0] = 0.0;
605     (*cost)[0] = 1.0;
606
607     for (i=1; i<size; i++)
608     {
609         (*sint)[i] = sin(angle*i);
610         (*cost)[i] = cos(angle*i);
611     }
612
613     /* Last sample is duplicate of the first */
614
615     (*sint)[size] = (*sint)[0];
616     (*cost)[size] = (*cost)[0];
617 }
618
619
620 /* -- INTERNAL DRAWING functions --------------------------------------- */
621 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
622     static void fgh##nameICaps( GLboolean useWireMode )\
623     {\
624         if (!name##Cached)\
625         {\
626             fgh##nameICaps##Generate();\
627             name##Cached = GL_TRUE;\
628         }\
629         \
630         if (useWireMode)\
631         {\
632             fghDrawGeometryWire (name##_verts,name##_norms,\
633                                                              nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE);\
634         }\
635         else\
636         {\
637             fghDrawGeometrySolid(name##_verts,name##_norms,vertIdxs,\
638                                  nameCaps##_VERT_PER_OBJ_TRI,                     nameCaps##_NUM_EDGE_PER_FACE);\
639         }\
640     }
641 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps)                        _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
642 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
643
644 static void fghCube( GLdouble dSize, GLboolean useWireMode )
645 {
646     GLdouble *vertices;
647
648     if (!cubeCached)
649     {
650         fghCubeGenerate();
651         cubeCached = GL_TRUE;
652     }
653
654     if (dSize!=1.)
655     {
656         /* Need to build new vertex list containing vertices for cube of different size */
657         int i;
658
659         vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLdouble));
660
661         /* Bail out if memory allocation fails, fgError never returns */
662         if (!vertices)
663         {
664             free(vertices);
665             fgError("Failed to allocate memory in fghCube");
666         }
667
668         for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
669             vertices[i] = dSize*cube_verts[i];
670     }
671     else
672         vertices = cube_verts;
673
674     if (useWireMode)
675         fghDrawGeometryWire (vertices  ,cube_norms,                                    CUBE_NUM_FACES,CUBE_NUM_EDGE_PER_FACE);
676     else
677         fghDrawGeometrySolid(vertices  ,cube_norms,cube_vertIdxs,CUBE_VERT_PER_OBJ_TRI,               CUBE_NUM_EDGE_PER_FACE);
678
679     if (dSize!=1.)
680         /* cleanup allocated memory */
681         free(vertices);
682 }
683
684 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON);
685 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON);
686 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON);
687 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON);
688 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON);
689
690 static void fghSierpinskiSponge ( int numLevels, GLdouble offset[3], GLdouble scale, GLboolean useWireMode )
691 {
692     GLdouble *vertices;
693     GLdouble * normals;
694     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
695     GLsizei    numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
696     GLsizei    numFace = numTetr*TETRAHEDRON_NUM_FACES;
697
698     if (numTetr)
699     {
700         /* Allocate memory */
701         vertices = malloc(numVert*3 * sizeof(GLdouble));
702         normals  = malloc(numVert*3 * sizeof(GLdouble));
703         /* Bail out if memory allocation fails, fgError never returns */
704         if (!vertices || !normals)
705         {
706             free(vertices);
707             free(normals);
708             fgError("Failed to allocate memory in fghSierpinskiSponge");
709         }
710
711         /* Generate elements */
712         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
713
714         /* Draw and cleanup */
715         if (useWireMode)
716             fghDrawGeometryWire (vertices,normals,             numFace,TETRAHEDRON_NUM_EDGE_PER_FACE);
717         else
718             fghDrawGeometrySolid(vertices,normals,NULL,numVert,        TETRAHEDRON_NUM_EDGE_PER_FACE);
719
720         free(vertices);
721         free(normals );
722     }
723 }
724
725
726 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
727
728
729 /*
730  * Draws a solid sphere
731  */
732 void FGAPIENTRY glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
733 {
734     int i,j;
735
736     /* Adjust z and radius as stacks are drawn. */
737
738     double z0,z1;
739     double r0,r1;
740
741     /* Pre-computed circle */
742
743     double *sint1,*cost1;
744     double *sint2,*cost2;
745
746     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
747
748     fghCircleTable(&sint1,&cost1,-slices);
749     fghCircleTable(&sint2,&cost2,stacks*2);
750
751     /* The top stack is covered with a triangle fan */
752
753     z0 = 1.0;
754     z1 = cost2[(stacks>0)?1:0];
755     r0 = 0.0;
756     r1 = sint2[(stacks>0)?1:0];
757
758     glBegin(GL_TRIANGLE_FAN);
759
760         glNormal3d(0,0,1);
761         glVertex3d(0,0,radius);
762
763         for (j=slices; j>=0; j--)
764         {
765             glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
766             glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
767         }
768
769     glEnd();
770
771     /* Cover each stack with a quad strip, except the top and bottom stacks */
772
773     for( i=1; i<stacks-1; i++ )
774     {
775         z0 = z1; z1 = cost2[i+1];
776         r0 = r1; r1 = sint2[i+1];
777
778         glBegin(GL_QUAD_STRIP);
779
780             for(j=0; j<=slices; j++)
781             {
782                 glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
783                 glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
784                 glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
785                 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
786             }
787
788         glEnd();
789     }
790
791     /* The bottom stack is covered with a triangle fan */
792
793     z0 = z1;
794     r0 = r1;
795
796     glBegin(GL_TRIANGLE_FAN);
797
798         glNormal3d(0,0,-1);
799         glVertex3d(0,0,-radius);
800
801         for (j=0; j<=slices; j++)
802         {
803             glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
804             glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
805         }
806
807     glEnd();
808
809     /* Release sin and cos tables */
810
811     free(sint1);
812     free(cost1);
813     free(sint2);
814     free(cost2);
815 }
816
817 /*
818  * Draws a wire sphere
819  */
820 void FGAPIENTRY glutWireSphere(GLdouble radius, GLint slices, GLint stacks)
821 {
822     int i,j;
823
824     /* Adjust z and radius as stacks and slices are drawn. */
825
826     double r;
827     double x,y,z;
828
829     /* Pre-computed circle */
830
831     double *sint1,*cost1;
832     double *sint2,*cost2;
833
834     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
835
836     fghCircleTable(&sint1,&cost1,-slices  );
837     fghCircleTable(&sint2,&cost2, stacks*2);
838
839     /* Draw a line loop for each stack */
840
841     for (i=1; i<stacks; i++)
842     {
843         z = cost2[i];
844         r = sint2[i];
845
846         glBegin(GL_LINE_LOOP);
847
848             for(j=0; j<=slices; j++)
849             {
850                 x = cost1[j];
851                 y = sint1[j];
852
853                 glNormal3d(x,y,z);
854                 glVertex3d(x*r*radius,y*r*radius,z*radius);
855             }
856
857         glEnd();
858     }
859
860     /* Draw a line loop for each slice */
861
862     for (i=0; i<slices; i++)
863     {
864         glBegin(GL_LINE_STRIP);
865
866             for(j=0; j<=stacks; j++)
867             {
868                 x = cost1[i]*sint2[j];
869                 y = sint1[i]*sint2[j];
870                 z = cost2[j];
871
872                 glNormal3d(x,y,z);
873                 glVertex3d(x*radius,y*radius,z*radius);
874             }
875
876         glEnd();
877     }
878
879     /* Release sin and cos tables */
880
881     free(sint1);
882     free(cost1);
883     free(sint2);
884     free(cost2);
885 }
886
887 /*
888  * Draws a solid cone
889  */
890 void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
891 {
892     int i,j;
893
894     /* Step in z and radius as stacks are drawn. */
895
896     double z0,z1;
897     double r0,r1;
898
899     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
900     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
901
902     /* Scaling factors for vertex normals */
903
904     const double cosn = ( height / sqrt ( height * height + base * base ));
905     const double sinn = ( base   / sqrt ( height * height + base * base ));
906
907     /* Pre-computed circle */
908
909     double *sint,*cost;
910
911     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
912
913     fghCircleTable(&sint,&cost,-slices);
914
915     /* Cover the circular base with a triangle fan... */
916
917     z0 = 0.0;
918     z1 = zStep;
919
920     r0 = base;
921     r1 = r0 - rStep;
922
923     glBegin(GL_TRIANGLE_FAN);
924
925         glNormal3d(0.0,0.0,-1.0);
926         glVertex3d(0.0,0.0, z0 );
927
928         for (j=0; j<=slices; j++)
929             glVertex3d(cost[j]*r0, sint[j]*r0, z0);
930
931     glEnd();
932
933     /* Cover each stack with a quad strip, except the top stack */
934
935     for( i=0; i<stacks-1; i++ )
936     {
937         glBegin(GL_QUAD_STRIP);
938
939             for(j=0; j<=slices; j++)
940             {
941                 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
942                 glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
943                 glVertex3d(cost[j]*r1,   sint[j]*r1,   z1  );
944             }
945
946             z0 = z1; z1 += zStep;
947             r0 = r1; r1 -= rStep;
948
949         glEnd();
950     }
951
952     /* The top stack is covered with individual triangles */
953
954     glBegin(GL_TRIANGLES);
955
956         glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
957
958         for (j=0; j<slices; j++)
959         {
960             glVertex3d(cost[j+0]*r0,   sint[j+0]*r0,   z0    );
961             glVertex3d(0,              0,              height);
962             glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn  );
963             glVertex3d(cost[j+1]*r0,   sint[j+1]*r0,   z0    );
964         }
965
966     glEnd();
967
968     /* Release sin and cos tables */
969
970     free(sint);
971     free(cost);
972 }
973
974 /*
975  * Draws a wire cone
976  */
977 void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks)
978 {
979     int i,j;
980
981     /* Step in z and radius as stacks are drawn. */
982
983     double z = 0.0;
984     double r = base;
985
986     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
987     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
988
989     /* Scaling factors for vertex normals */
990
991     const double cosn = ( height / sqrt ( height * height + base * base ));
992     const double sinn = ( base   / sqrt ( height * height + base * base ));
993
994     /* Pre-computed circle */
995
996     double *sint,*cost;
997
998     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
999
1000     fghCircleTable(&sint,&cost,-slices);
1001
1002     /* Draw the stacks... */
1003
1004     for (i=0; i<stacks; i++)
1005     {
1006         glBegin(GL_LINE_LOOP);
1007
1008             for( j=0; j<slices; j++ )
1009             {
1010                 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
1011                 glVertex3d(cost[j]*r,    sint[j]*r,    z   );
1012             }
1013
1014         glEnd();
1015
1016         z += zStep;
1017         r -= rStep;
1018     }
1019
1020     /* Draw the slices */
1021
1022     r = base;
1023
1024     glBegin(GL_LINES);
1025
1026         for (j=0; j<slices; j++)
1027         {
1028             glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn  );
1029             glVertex3d(cost[j]*r,    sint[j]*r,    0.0   );
1030             glVertex3d(0.0,          0.0,          height);
1031         }
1032
1033     glEnd();
1034
1035     /* Release sin and cos tables */
1036
1037     free(sint);
1038     free(cost);
1039 }
1040
1041
1042 /*
1043  * Draws a solid cylinder
1044  */
1045 void FGAPIENTRY glutSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1046 {
1047     int i,j;
1048
1049     /* Step in z and radius as stacks are drawn. */
1050
1051     double z0,z1;
1052     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1053
1054     /* Pre-computed circle */
1055
1056     double *sint,*cost;
1057
1058     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1059
1060     fghCircleTable(&sint,&cost,-slices);
1061
1062     /* Cover the base and top */
1063
1064     glBegin(GL_TRIANGLE_FAN);
1065         glNormal3d(0.0, 0.0, -1.0 );
1066         glVertex3d(0.0, 0.0,  0.0 );
1067         for (j=0; j<=slices; j++)
1068           glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
1069     glEnd();
1070
1071     glBegin(GL_TRIANGLE_FAN);
1072         glNormal3d(0.0, 0.0, 1.0   );
1073         glVertex3d(0.0, 0.0, height);
1074         for (j=slices; j>=0; j--)
1075           glVertex3d(cost[j]*radius, sint[j]*radius, height);
1076     glEnd();
1077
1078     /* Do the stacks */
1079
1080     z0 = 0.0;
1081     z1 = zStep;
1082
1083     for (i=1; i<=stacks; i++)
1084     {
1085         if (i==stacks)
1086             z1 = height;
1087
1088         glBegin(GL_QUAD_STRIP);
1089             for (j=0; j<=slices; j++ )
1090             {
1091                 glNormal3d(cost[j],        sint[j],        0.0 );
1092                 glVertex3d(cost[j]*radius, sint[j]*radius, z0  );
1093                 glVertex3d(cost[j]*radius, sint[j]*radius, z1  );
1094             }
1095         glEnd();
1096
1097         z0 = z1; z1 += zStep;
1098     }
1099
1100     /* Release sin and cos tables */
1101
1102     free(sint);
1103     free(cost);
1104 }
1105
1106 /*
1107  * Draws a wire cylinder
1108  */
1109 void FGAPIENTRY glutWireCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1110 {
1111     int i,j;
1112
1113     /* Step in z and radius as stacks are drawn. */
1114
1115           double z = 0.0;
1116     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1117
1118     /* Pre-computed circle */
1119
1120     double *sint,*cost;
1121
1122     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1123
1124     fghCircleTable(&sint,&cost,-slices);
1125
1126     /* Draw the stacks... */
1127
1128     for (i=0; i<=stacks; i++)
1129     {
1130         if (i==stacks)
1131             z = height;
1132
1133         glBegin(GL_LINE_LOOP);
1134
1135             for( j=0; j<slices; j++ )
1136             {
1137                 glNormal3d(cost[j],        sint[j],        0.0);
1138                 glVertex3d(cost[j]*radius, sint[j]*radius, z  );
1139             }
1140
1141         glEnd();
1142
1143         z += zStep;
1144     }
1145
1146     /* Draw the slices */
1147
1148     glBegin(GL_LINES);
1149
1150         for (j=0; j<slices; j++)
1151         {
1152             glNormal3d(cost[j],        sint[j],        0.0   );
1153             glVertex3d(cost[j]*radius, sint[j]*radius, 0.0   );
1154             glVertex3d(cost[j]*radius, sint[j]*radius, height);
1155         }
1156
1157     glEnd();
1158
1159     /* Release sin and cos tables */
1160
1161     free(sint);
1162     free(cost);
1163 }
1164
1165 /*
1166  * Draws a wire torus
1167  */
1168 void FGAPIENTRY glutWireTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1169 {
1170   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1171   double *vertex, *normal;
1172   int    i, j;
1173   double spsi, cpsi, sphi, cphi ;
1174
1175   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1176
1177   if ( nSides < 1 ) nSides = 1;
1178   if ( nRings < 1 ) nRings = 1;
1179
1180   /* Allocate the vertices array */
1181   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1182   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1183
1184   glPushMatrix();
1185
1186   dpsi =  2.0 * M_PI / (double)nRings ;
1187   dphi = -2.0 * M_PI / (double)nSides ;
1188   psi  = 0.0;
1189
1190   for( j=0; j<nRings; j++ )
1191   {
1192     cpsi = cos ( psi ) ;
1193     spsi = sin ( psi ) ;
1194     phi = 0.0;
1195
1196     for( i=0; i<nSides; i++ )
1197     {
1198       int offset = 3 * ( j * nSides + i ) ;
1199       cphi = cos ( phi ) ;
1200       sphi = sin ( phi ) ;
1201       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1202       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1203       *(vertex + offset + 2) =                    sphi * iradius  ;
1204       *(normal + offset + 0) = cpsi * cphi ;
1205       *(normal + offset + 1) = spsi * cphi ;
1206       *(normal + offset + 2) =        sphi ;
1207       phi += dphi;
1208     }
1209
1210     psi += dpsi;
1211   }
1212
1213   for( i=0; i<nSides; i++ )
1214   {
1215     glBegin( GL_LINE_LOOP );
1216
1217     for( j=0; j<nRings; j++ )
1218     {
1219       int offset = 3 * ( j * nSides + i ) ;
1220       glNormal3dv( normal + offset );
1221       glVertex3dv( vertex + offset );
1222     }
1223
1224     glEnd();
1225   }
1226
1227   for( j=0; j<nRings; j++ )
1228   {
1229     glBegin(GL_LINE_LOOP);
1230
1231     for( i=0; i<nSides; i++ )
1232     {
1233       int offset = 3 * ( j * nSides + i ) ;
1234       glNormal3dv( normal + offset );
1235       glVertex3dv( vertex + offset );
1236     }
1237
1238     glEnd();
1239   }
1240
1241   free ( vertex ) ;
1242   free ( normal ) ;
1243   glPopMatrix();
1244 }
1245
1246 /*
1247  * Draws a solid torus
1248  */
1249 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1250 {
1251   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1252   double *vertex, *normal;
1253   int    i, j;
1254   double spsi, cpsi, sphi, cphi ;
1255
1256   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1257
1258   if ( nSides < 1 ) nSides = 1;
1259   if ( nRings < 1 ) nRings = 1;
1260
1261   /* Increment the number of sides and rings to allow for one more point than surface */
1262   nSides ++ ;
1263   nRings ++ ;
1264
1265   /* Allocate the vertices array */
1266   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1267   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1268
1269   glPushMatrix();
1270
1271   dpsi =  2.0 * M_PI / (double)(nRings - 1) ;
1272   dphi = -2.0 * M_PI / (double)(nSides - 1) ;
1273   psi  = 0.0;
1274
1275   for( j=0; j<nRings; j++ )
1276   {
1277     cpsi = cos ( psi ) ;
1278     spsi = sin ( psi ) ;
1279     phi = 0.0;
1280
1281     for( i=0; i<nSides; i++ )
1282     {
1283       int offset = 3 * ( j * nSides + i ) ;
1284       cphi = cos ( phi ) ;
1285       sphi = sin ( phi ) ;
1286       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1287       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1288       *(vertex + offset + 2) =                    sphi * iradius  ;
1289       *(normal + offset + 0) = cpsi * cphi ;
1290       *(normal + offset + 1) = spsi * cphi ;
1291       *(normal + offset + 2) =        sphi ;
1292       phi += dphi;
1293     }
1294
1295     psi += dpsi;
1296   }
1297
1298     glBegin( GL_QUADS );
1299   for( i=0; i<nSides-1; i++ )
1300   {
1301     for( j=0; j<nRings-1; j++ )
1302     {
1303       int offset = 3 * ( j * nSides + i ) ;
1304       glNormal3dv( normal + offset );
1305       glVertex3dv( vertex + offset );
1306       glNormal3dv( normal + offset + 3 );
1307       glVertex3dv( vertex + offset + 3 );
1308       glNormal3dv( normal + offset + 3 * nSides + 3 );
1309       glVertex3dv( vertex + offset + 3 * nSides + 3 );
1310       glNormal3dv( normal + offset + 3 * nSides );
1311       glVertex3dv( vertex + offset + 3 * nSides );
1312     }
1313   }
1314
1315   glEnd();
1316
1317   free ( vertex ) ;
1318   free ( normal ) ;
1319   glPopMatrix();
1320 }
1321
1322
1323
1324 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1325 /* Macro to generate interface functions */
1326 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1327     void FGAPIENTRY glutWire##nameICaps( void )\
1328     {\
1329         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1330         fgh##nameICaps( TRUE );\
1331     }\
1332     void FGAPIENTRY glutSolid##nameICaps( void )\
1333     {\
1334         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1335         fgh##nameICaps( FALSE );\
1336     }
1337
1338 void FGAPIENTRY glutWireCube( GLdouble dSize )
1339 {
1340     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1341     fghCube( dSize, TRUE );
1342 }
1343 void FGAPIENTRY glutSolidCube( GLdouble dSize )
1344 {
1345     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1346     fghCube( dSize, FALSE );
1347 }
1348
1349 DECLARE_SHAPE_INTERFACE(Dodecahedron);
1350 DECLARE_SHAPE_INTERFACE(Icosahedron);
1351 DECLARE_SHAPE_INTERFACE(Octahedron);
1352 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron);
1353
1354 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1355 {
1356     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1357     fghSierpinskiSponge ( num_levels, offset, scale, TRUE );
1358 }
1359 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1360 {
1361     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1362     fghSierpinskiSponge ( num_levels, offset, scale, FALSE );
1363 }
1364
1365 DECLARE_SHAPE_INTERFACE(Tetrahedron);
1366
1367
1368 /*** END OF FILE ***/