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