All doubles now GLdoubles (important for GLES later)
[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                 printf("j(%i):%1.3f\n",j,z);
876
877                 glNormal3d(x,y,z);
878                 glVertex3d(x*radius,y*radius,z*radius);
879             }
880
881         glEnd();
882     }
883
884     /* Release sin and cos tables */
885
886     free(sint1);
887     free(cost1);
888     free(sint2);
889     free(cost2);
890 }
891
892 /*
893  * Draws a solid cone
894  */
895 void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
896 {
897     int i,j;
898
899     /* Step in z and radius as stacks are drawn. */
900
901     GLdouble z0,z1;
902     GLdouble r0,r1;
903
904     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
905     const GLdouble rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
906
907     /* Scaling factors for vertex normals */
908
909     const GLdouble cosn = ( height / sqrt ( height * height + base * base ));
910     const GLdouble sinn = ( base   / sqrt ( height * height + base * base ));
911
912     /* Pre-computed circle */
913
914     GLdouble *sint,*cost;
915
916     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
917
918     fghCircleTable(&sint,&cost,-slices,FALSE);
919
920     /* Cover the circular base with a triangle fan... */
921
922     z0 = 0.0;
923     z1 = zStep;
924
925     r0 = base;
926     r1 = r0 - rStep;
927
928     glBegin(GL_TRIANGLE_FAN);
929
930         glNormal3d(0.0,0.0,-1.0);
931         glVertex3d(0.0,0.0, z0 );
932
933         for (j=0; j<=slices; j++)
934             glVertex3d(cost[j]*r0, sint[j]*r0, z0);
935
936     glEnd();
937
938     /* Cover each stack with a quad strip, except the top stack */
939
940     for( i=0; i<stacks-1; i++ )
941     {
942         glBegin(GL_QUAD_STRIP);
943
944             for(j=0; j<=slices; j++)
945             {
946                 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
947                 glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
948                 glVertex3d(cost[j]*r1,   sint[j]*r1,   z1  );
949             }
950
951             z0 = z1; z1 += zStep;
952             r0 = r1; r1 -= rStep;
953
954         glEnd();
955     }
956
957     /* The top stack is covered with individual triangles */
958
959     glBegin(GL_TRIANGLES);
960
961         glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
962
963         for (j=0; j<slices; j++)
964         {
965             glVertex3d(cost[j+0]*r0,   sint[j+0]*r0,   z0    );
966             glVertex3d(0,              0,              height);
967             glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn  );
968             glVertex3d(cost[j+1]*r0,   sint[j+1]*r0,   z0    );
969         }
970
971     glEnd();
972
973     /* Release sin and cos tables */
974
975     free(sint);
976     free(cost);
977 }
978
979 /*
980  * Draws a wire cone
981  */
982 void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks)
983 {
984     int i,j;
985
986     /* Step in z and radius as stacks are drawn. */
987
988     GLdouble z = 0.0;
989     GLdouble r = base;
990
991     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
992     const GLdouble rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
993
994     /* Scaling factors for vertex normals */
995
996     const GLdouble cosn = ( height / sqrt ( height * height + base * base ));
997     const GLdouble sinn = ( base   / sqrt ( height * height + base * base ));
998
999     /* Pre-computed circle */
1000
1001     GLdouble *sint,*cost;
1002
1003     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1004
1005     fghCircleTable(&sint,&cost,-slices,FALSE);
1006
1007     /* Draw the stacks... */
1008
1009     for (i=0; i<stacks; i++)
1010     {
1011         glBegin(GL_LINE_LOOP);
1012
1013             for( j=0; j<slices; j++ )
1014             {
1015                 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
1016                 glVertex3d(cost[j]*r,    sint[j]*r,    z   );
1017             }
1018
1019         glEnd();
1020
1021         z += zStep;
1022         r -= rStep;
1023     }
1024
1025     /* Draw the slices */
1026
1027     r = base;
1028
1029     glBegin(GL_LINES);
1030
1031         for (j=0; j<slices; j++)
1032         {
1033             glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn  );
1034             glVertex3d(cost[j]*r,    sint[j]*r,    0.0   );
1035             glVertex3d(0.0,          0.0,          height);
1036         }
1037
1038     glEnd();
1039
1040     /* Release sin and cos tables */
1041
1042     free(sint);
1043     free(cost);
1044 }
1045
1046
1047 /*
1048  * Draws a solid cylinder
1049  */
1050 void FGAPIENTRY glutSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1051 {
1052     int i,j;
1053
1054     /* Step in z and radius as stacks are drawn. */
1055
1056     GLdouble z0,z1;
1057     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1058
1059     /* Pre-computed circle */
1060
1061     GLdouble *sint,*cost;
1062
1063     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1064
1065     fghCircleTable(&sint,&cost,-slices,FALSE);
1066
1067     /* Cover the base and top */
1068
1069     glBegin(GL_TRIANGLE_FAN);
1070         glNormal3d(0.0, 0.0, -1.0 );
1071         glVertex3d(0.0, 0.0,  0.0 );
1072         for (j=0; j<=slices; j++)
1073           glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
1074     glEnd();
1075
1076     glBegin(GL_TRIANGLE_FAN);
1077         glNormal3d(0.0, 0.0, 1.0   );
1078         glVertex3d(0.0, 0.0, height);
1079         for (j=slices; j>=0; j--)
1080           glVertex3d(cost[j]*radius, sint[j]*radius, height);
1081     glEnd();
1082
1083     /* Do the stacks */
1084
1085     z0 = 0.0;
1086     z1 = zStep;
1087
1088     for (i=1; i<=stacks; i++)
1089     {
1090         if (i==stacks)
1091             z1 = height;
1092
1093         glBegin(GL_QUAD_STRIP);
1094             for (j=0; j<=slices; j++ )
1095             {
1096                 glNormal3d(cost[j],        sint[j],        0.0 );
1097                 glVertex3d(cost[j]*radius, sint[j]*radius, z0  );
1098                 glVertex3d(cost[j]*radius, sint[j]*radius, z1  );
1099             }
1100         glEnd();
1101
1102         z0 = z1; z1 += zStep;
1103     }
1104
1105     /* Release sin and cos tables */
1106
1107     free(sint);
1108     free(cost);
1109 }
1110
1111 /*
1112  * Draws a wire cylinder
1113  */
1114 void FGAPIENTRY glutWireCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1115 {
1116     int i,j;
1117
1118     /* Step in z and radius as stacks are drawn. */
1119
1120           GLdouble z = 0.0;
1121     const GLdouble zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1122
1123     /* Pre-computed circle */
1124
1125     GLdouble *sint,*cost;
1126
1127     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1128
1129     fghCircleTable(&sint,&cost,-slices,FALSE);
1130
1131     /* Draw the stacks... */
1132
1133     for (i=0; i<=stacks; i++)
1134     {
1135         if (i==stacks)
1136             z = height;
1137
1138         glBegin(GL_LINE_LOOP);
1139
1140             for( j=0; j<slices; j++ )
1141             {
1142                 glNormal3d(cost[j],        sint[j],        0.0);
1143                 glVertex3d(cost[j]*radius, sint[j]*radius, z  );
1144             }
1145
1146         glEnd();
1147
1148         z += zStep;
1149     }
1150
1151     /* Draw the slices */
1152
1153     glBegin(GL_LINES);
1154
1155         for (j=0; j<slices; j++)
1156         {
1157             glNormal3d(cost[j],        sint[j],        0.0   );
1158             glVertex3d(cost[j]*radius, sint[j]*radius, 0.0   );
1159             glVertex3d(cost[j]*radius, sint[j]*radius, height);
1160         }
1161
1162     glEnd();
1163
1164     /* Release sin and cos tables */
1165
1166     free(sint);
1167     free(cost);
1168 }
1169
1170 /*
1171  * Draws a wire torus
1172  */
1173 void FGAPIENTRY glutWireTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1174 {
1175   GLdouble  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1176   GLdouble *vertex, *normal;
1177   int    i, j;
1178   GLdouble spsi, cpsi, sphi, cphi ;
1179
1180   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1181
1182   if ( nSides < 1 ) nSides = 1;
1183   if ( nRings < 1 ) nRings = 1;
1184
1185   /* Allocate the vertices array */
1186   vertex = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1187   normal = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1188
1189   glPushMatrix();
1190
1191   dpsi =  2.0 * M_PI / (GLdouble)nRings ;
1192   dphi = -2.0 * M_PI / (GLdouble)nSides ;
1193   psi  = 0.0;
1194
1195   for( j=0; j<nRings; j++ )
1196   {
1197     cpsi = cos ( psi ) ;
1198     spsi = sin ( psi ) ;
1199     phi = 0.0;
1200
1201     for( i=0; i<nSides; i++ )
1202     {
1203       int offset = 3 * ( j * nSides + i ) ;
1204       cphi = cos ( phi ) ;
1205       sphi = sin ( phi ) ;
1206       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1207       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1208       *(vertex + offset + 2) =                    sphi * iradius  ;
1209       *(normal + offset + 0) = cpsi * cphi ;
1210       *(normal + offset + 1) = spsi * cphi ;
1211       *(normal + offset + 2) =        sphi ;
1212       phi += dphi;
1213     }
1214
1215     psi += dpsi;
1216   }
1217
1218   for( i=0; i<nSides; i++ )
1219   {
1220     glBegin( GL_LINE_LOOP );
1221
1222     for( j=0; j<nRings; j++ )
1223     {
1224       int offset = 3 * ( j * nSides + i ) ;
1225       glNormal3dv( normal + offset );
1226       glVertex3dv( vertex + offset );
1227     }
1228
1229     glEnd();
1230   }
1231
1232   for( j=0; j<nRings; j++ )
1233   {
1234     glBegin(GL_LINE_LOOP);
1235
1236     for( i=0; i<nSides; i++ )
1237     {
1238       int offset = 3 * ( j * nSides + i ) ;
1239       glNormal3dv( normal + offset );
1240       glVertex3dv( vertex + offset );
1241     }
1242
1243     glEnd();
1244   }
1245
1246   free ( vertex ) ;
1247   free ( normal ) ;
1248   glPopMatrix();
1249 }
1250
1251 /*
1252  * Draws a solid torus
1253  */
1254 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1255 {
1256   GLdouble  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1257   GLdouble *vertex, *normal;
1258   int    i, j;
1259   GLdouble spsi, cpsi, sphi, cphi ;
1260
1261   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1262
1263   if ( nSides < 1 ) nSides = 1;
1264   if ( nRings < 1 ) nRings = 1;
1265
1266   /* Increment the number of sides and rings to allow for one more point than surface */
1267   nSides ++ ;
1268   nRings ++ ;
1269
1270   /* Allocate the vertices array */
1271   vertex = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1272   normal = (GLdouble *)calloc( sizeof(GLdouble), 3 * nSides * nRings );
1273
1274   glPushMatrix();
1275
1276   dpsi =  2.0 * M_PI / (GLdouble)(nRings - 1) ;
1277   dphi = -2.0 * M_PI / (GLdouble)(nSides - 1) ;
1278   psi  = 0.0;
1279
1280   for( j=0; j<nRings; j++ )
1281   {
1282     cpsi = cos ( psi ) ;
1283     spsi = sin ( psi ) ;
1284     phi = 0.0;
1285
1286     for( i=0; i<nSides; i++ )
1287     {
1288       int offset = 3 * ( j * nSides + i ) ;
1289       cphi = cos ( phi ) ;
1290       sphi = sin ( phi ) ;
1291       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1292       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1293       *(vertex + offset + 2) =                    sphi * iradius  ;
1294       *(normal + offset + 0) = cpsi * cphi ;
1295       *(normal + offset + 1) = spsi * cphi ;
1296       *(normal + offset + 2) =        sphi ;
1297       phi += dphi;
1298     }
1299
1300     psi += dpsi;
1301   }
1302
1303     glBegin( GL_QUADS );
1304   for( i=0; i<nSides-1; i++ )
1305   {
1306     for( j=0; j<nRings-1; j++ )
1307     {
1308       int offset = 3 * ( j * nSides + i ) ;
1309       glNormal3dv( normal + offset );
1310       glVertex3dv( vertex + offset );
1311       glNormal3dv( normal + offset + 3 );
1312       glVertex3dv( vertex + offset + 3 );
1313       glNormal3dv( normal + offset + 3 * nSides + 3 );
1314       glVertex3dv( vertex + offset + 3 * nSides + 3 );
1315       glNormal3dv( normal + offset + 3 * nSides );
1316       glVertex3dv( vertex + offset + 3 * nSides );
1317     }
1318   }
1319
1320   glEnd();
1321
1322   free ( vertex ) ;
1323   free ( normal ) ;
1324   glPopMatrix();
1325 }
1326
1327
1328
1329 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1330 /* Macro to generate interface functions */
1331 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1332     void FGAPIENTRY glutWire##nameICaps( void )\
1333     {\
1334         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1335         fgh##nameICaps( TRUE );\
1336     }\
1337     void FGAPIENTRY glutSolid##nameICaps( void )\
1338     {\
1339         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1340         fgh##nameICaps( FALSE );\
1341     }
1342
1343 void FGAPIENTRY glutWireCube( GLdouble dSize )
1344 {
1345     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1346     fghCube( dSize, TRUE );
1347 }
1348 void FGAPIENTRY glutSolidCube( GLdouble dSize )
1349 {
1350     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1351     fghCube( dSize, FALSE );
1352 }
1353
1354 DECLARE_SHAPE_INTERFACE(Dodecahedron);
1355 DECLARE_SHAPE_INTERFACE(Icosahedron);
1356 DECLARE_SHAPE_INTERFACE(Octahedron);
1357 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron);
1358
1359 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1360 {
1361     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1362     fghSierpinskiSponge ( num_levels, offset, scale, TRUE );
1363 }
1364 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1365 {
1366     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1367     fghSierpinskiSponge ( num_levels, offset, scale, FALSE );
1368 }
1369
1370 DECLARE_SHAPE_INTERFACE(Tetrahedron);
1371
1372
1373 /*** END OF FILE ***/