e589d4c2ed77be693bf05fa22f22e18af27e95d8
[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
32 /*
33  * Need more types of polyhedra? See CPolyhedron in MRPT
34  */
35
36
37 /* General functions for drawing geometry
38  * Solids are drawn by glDrawArrays if composed of triangles, or by
39  * glDrawElements if consisting of squares or pentagons that were
40  * decomposed into triangles (some vertices are repeated in that case).
41  * WireFrame drawing will have to be done per face, using GL_LINE_LOOP and
42  * issuing one draw call per face. Always use glDrawArrays as no triangle
43  * decomposition needed. We use the "first" parameter in glDrawArrays to go
44  * from face to face.
45  */
46
47 /* Version for OpenGL (ES) 1.1 */
48 #ifndef GL_ES_VERSION_2_0
49 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals, GLsizei numFaces, GLsizei numEdgePerFace)
50 {
51     int i;
52     
53     glEnableClientState(GL_VERTEX_ARRAY);
54     glEnableClientState(GL_NORMAL_ARRAY);
55
56     glVertexPointer(3, GL_FLOAT, 0, vertices);
57     glNormalPointer(GL_FLOAT, 0, normals);
58
59     /* Draw per face (TODO: could use glMultiDrawArrays if available) */
60     for (i=0; i<numFaces; i++)
61         glDrawArrays(GL_LINE_LOOP, i*numEdgePerFace, numEdgePerFace);
62
63     glDisableClientState(GL_VERTEX_ARRAY);
64     glDisableClientState(GL_NORMAL_ARRAY);
65 }
66 #endif
67
68 /* Version for OpenGL (ES) >= 2.0 */
69 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numFaces, GLsizei numEdgePerFace,
70                                   GLint attribute_v_coord, GLint attribute_v_normal)
71 {
72     GLuint vbo_coords, vbo_normals;
73     GLuint numVertices = numFaces * numEdgePerFace;
74     
75     if (numVertices > 0 && attribute_v_coord != -1) {
76             fghGenBuffers(1, &vbo_coords);
77             fghBindBuffer(GL_ARRAY_BUFFER, vbo_coords);
78             fghBufferData(GL_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
79                           vertices, GL_STATIC_DRAW);
80     }
81     
82     if (numVertices > 0 && attribute_v_normal != -1) {
83         fghGenBuffers(1, &vbo_normals);
84         fghBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
85         fghBufferData(GL_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
86                       normals, GL_STATIC_DRAW);
87     }
88     
89     if (vbo_coords) {
90         fghEnableVertexAttribArray(attribute_v_coord);
91         fghBindBuffer(GL_ARRAY_BUFFER, vbo_coords);
92         fghVertexAttribPointer(
93             attribute_v_coord,  // attribute
94             3,                  // number of elements per vertex, here (x,y,z)
95             GL_FLOAT,           // the type of each element
96             GL_FALSE,           // take our values as-is
97             0,                  // no extra data between each position
98             0                   // offset of first element
99         );
100     }
101
102     if (vbo_normals) {
103         fghEnableVertexAttribArray(attribute_v_normal);
104         fghBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
105         fghVertexAttribPointer(
106             attribute_v_normal, // attribute
107             3,                  // number of elements per vertex, here (x,y,z)
108             GL_FLOAT,           // the type of each element
109             GL_FALSE,           // take our values as-is
110             0,                  // no extra data between each position
111             0                   // offset of first element
112         );
113     }
114
115
116     int i;
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, vbo_normals, ibo_elements;
185     
186     if (numVertices > 0 && attribute_v_coord != -1) {
187         fghGenBuffers(1, &vbo_coords);
188         fghBindBuffer(GL_ARRAY_BUFFER, vbo_coords);
189         fghBufferData(GL_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
190                       vertices, GL_STATIC_DRAW);
191         }
192     
193     if (numVertices > 0 && attribute_v_normal != -1) {
194         fghGenBuffers(1, &vbo_normals);
195         fghBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
196         fghBufferData(GL_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
197                       normals, GL_STATIC_DRAW);
198     }
199     
200     if (vertIdxs != NULL) {
201         fghGenBuffers(1, &ibo_elements);
202         fghBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_elements);
203         fghBufferData(GL_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
204                       vertIdxs, GL_STATIC_DRAW);
205     }
206     
207     if (vbo_coords) {
208         fghEnableVertexAttribArray(attribute_v_coord);
209         fghBindBuffer(GL_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(GL_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(GL_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         (*sint)[i] = sinf(angle*i);
800         (*cost)[i] = cosf(angle*i);
801     }
802
803     
804     if (halfCircle)
805     {
806         (*sint)[size] =  0.0f;  /* sin PI */
807         (*cost)[size] = -1.0f;  /* cos PI */
808     }
809     else
810     {
811         /* Last sample is duplicate of the first (sin or cos of 2 PI) */
812         (*sint)[size] = (*sint)[0];
813         (*cost)[size] = (*cost)[0];
814     }
815 }
816
817 static void fghGenerateSphere(GLfloat radius, GLint slices, GLint stacks, GLfloat **vertices, GLfloat **normals, int* nVert)
818 {
819     int i,j;
820     int idx = 0;    /* idx into vertex/normal buffer */
821     GLfloat x,y,z;
822
823     /* Pre-computed circle */
824     GLfloat *sint1,*cost1;
825     GLfloat *sint2,*cost2;
826
827     /* number of unique vertices */
828     if (slices==0 || stacks<2)
829     {
830         /* nothing to generate */
831         *nVert = 0;
832         return;
833     }
834     *nVert = slices*(stacks-1)+2;
835
836     /* precompute values on unit circle */
837     fghCircleTable(&sint1,&cost1,-slices,FALSE);
838     fghCircleTable(&sint2,&cost2, stacks,TRUE);
839
840     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
841     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
842     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
843     if (!(vertices) || !(normals))
844     {
845         free(*vertices);
846         free(*normals);
847         fgError("Failed to allocate memory in fghGenerateSphere");
848     }
849
850     /* top */
851     (*vertices)[0] = 0.f;
852     (*vertices)[1] = 0.f;
853     (*vertices)[2] = radius;
854     (*normals )[0] = 0.f;
855     (*normals )[1] = 0.f;
856     (*normals )[2] = 1.f;
857     idx = 3;
858
859     /* each stack */
860     for( i=1; i<stacks; i++ )
861     {
862         for(j=0; j<slices; j++, idx+=3)
863         {
864             x = cost1[j]*sint2[i];
865             y = sint1[j]*sint2[i];
866             z = cost2[i];
867
868             (*vertices)[idx  ] = x*radius;
869             (*vertices)[idx+1] = y*radius;
870             (*vertices)[idx+2] = z*radius;
871             (*normals )[idx  ] = x;
872             (*normals )[idx+1] = y;
873             (*normals )[idx+2] = z;
874         }
875     }
876
877     /* bottom */
878     (*vertices)[idx  ] =  0.f;
879     (*vertices)[idx+1] =  0.f;
880     (*vertices)[idx+2] = -radius;
881     (*normals )[idx  ] =  0.f;
882     (*normals )[idx+1] =  0.f;
883     (*normals )[idx+2] = -1.f;
884
885     /* Done creating vertices, release sin and cos tables */
886     free(sint1);
887     free(cost1);
888     free(sint2);
889     free(cost2);
890 }
891
892
893 /* -- INTERNAL DRAWING functions --------------------------------------- */
894 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
895     static void fgh##nameICaps( GLboolean useWireMode )\
896     {\
897         if (!name##Cached)\
898         {\
899             fgh##nameICaps##Generate();\
900             name##Cached = GL_TRUE;\
901         }\
902         \
903         if (useWireMode)\
904         {\
905             fghDrawGeometryWire (name##_verts,name##_norms,\
906                                                              nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE);\
907         }\
908         else\
909         {\
910             fghDrawGeometrySolid(name##_verts,name##_norms,vertIdxs,\
911                                  nameCaps##_VERT_PER_OBJ, nameCaps##_VERT_PER_OBJ_TRI); \
912         }\
913     }
914 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps)                        _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
915 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
916
917 static void fghCube( GLfloat dSize, GLboolean useWireMode )
918 {
919     GLfloat *vertices;
920
921     if (!cubeCached)
922     {
923         fghCubeGenerate();
924         cubeCached = GL_TRUE;
925     }
926
927     if (dSize!=1.f)
928     {
929         /* Need to build new vertex list containing vertices for cube of different size */
930         int i;
931
932         vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
933
934         /* Bail out if memory allocation fails, fgError never returns */
935         if (!vertices)
936         {
937             free(vertices);
938             fgError("Failed to allocate memory in fghCube");
939         }
940
941         for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
942             vertices[i] = dSize*cube_verts[i];
943     }
944     else
945         vertices = cube_verts;
946
947     if (useWireMode)
948         fghDrawGeometryWire (vertices, cube_norms,
949                              CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE);
950     else
951         fghDrawGeometrySolid(vertices, cube_norms, cube_vertIdxs,
952                              CUBE_VERT_PER_OBJ, CUBE_VERT_PER_OBJ_TRI);
953
954     if (dSize!=1.f)
955         /* cleanup allocated memory */
956         free(vertices);
957 }
958
959 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON);
960 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON);
961 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON);
962 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON);
963 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON);
964
965 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
966 {
967     GLfloat *vertices;
968     GLfloat * normals;
969     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
970     GLsizei    numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
971     GLsizei    numFace = numTetr*TETRAHEDRON_NUM_FACES;
972
973     if (numTetr)
974     {
975         /* Allocate memory */
976         vertices = malloc(numVert*3 * sizeof(GLfloat));
977         normals  = malloc(numVert*3 * sizeof(GLfloat));
978         /* Bail out if memory allocation fails, fgError never returns */
979         if (!vertices || !normals)
980         {
981             free(vertices);
982             free(normals);
983             fgError("Failed to allocate memory in fghSierpinskiSponge");
984         }
985
986         /* Generate elements */
987         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
988
989         /* Draw and cleanup */
990         if (useWireMode)
991             fghDrawGeometryWire (vertices,normals,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE);
992         else
993             fghDrawGeometrySolid(vertices,normals,NULL,numVert,numVert);
994
995         free(vertices);
996         free(normals );
997     }
998 }
999
1000
1001 #ifndef GL_ES_VERSION_2_0
1002 static void fghSphere( double radius, GLint slices, GLint stacks, GLboolean useWireMode )
1003 {
1004     int i,j,idx, nVert;
1005     GLfloat *vertices, *normals;
1006
1007     if (slices * stacks > 65535)
1008         fgWarning("fghSphere: too many slices or stacks requested, indices will wrap");
1009
1010     /* Generate vertices and normals */
1011     fghGenerateSphere((GLfloat)radius,slices,stacks,&vertices,&normals,&nVert);
1012     
1013     if (nVert==0)
1014         /* nothing to draw */
1015         return;
1016
1017     if (useWireMode)
1018     {
1019         GLushort  *sliceIdx, *stackIdx;
1020         /* First, generate vertex index arrays for drawing with glDrawElements
1021          * We have a bunch of line_loops to draw for each stack, and a
1022          * bunch for each slice.
1023          */
1024
1025         sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1026         stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort));
1027
1028         /* generate for each stack */
1029         for (i=0,idx=0; i<slices; i++)
1030         {
1031             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1032             sliceIdx[idx++] = 0;                    /* vertex on top */
1033             for (j=0; j<stacks-1; j++, idx++)
1034             {
1035                 sliceIdx[idx] = offset+j*slices;
1036             }
1037             sliceIdx[idx++] = nVert-1;              /* zero based index, last element in array... */
1038         }
1039
1040         /* generate for each stack */
1041         for (i=0,idx=0; i<stacks-1; i++)
1042         {
1043             GLushort offset = 1+i*slices;           /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1044             for (j=0; j<slices; j++, idx++)
1045             {
1046                 stackIdx[idx] = offset+j;
1047             }
1048         }
1049
1050         /* draw */
1051         glEnableClientState(GL_VERTEX_ARRAY);
1052         glEnableClientState(GL_NORMAL_ARRAY);
1053
1054         glVertexPointer(3, GL_FLOAT, 0, vertices);
1055         glNormalPointer(GL_FLOAT, 0, normals);
1056         /*draw slices*/
1057         for (i=0; i<slices; i++)
1058             glDrawElements(GL_LINE_STRIP,stacks+1,GL_UNSIGNED_SHORT,sliceIdx+i*(stacks+1));
1059         /*draw stacks*/
1060         for (i=0; i<stacks-1; i++)
1061             glDrawElements(GL_LINE_LOOP, slices,GL_UNSIGNED_SHORT,stackIdx+i*slices);
1062
1063         glDisableClientState(GL_VERTEX_ARRAY);
1064         glDisableClientState(GL_NORMAL_ARRAY);
1065
1066         /* cleanup allocated memory */
1067         free(sliceIdx);
1068         free(stackIdx);
1069     }
1070     else
1071     {
1072         GLushort  *topIdx, *bottomIdx, *stripIdx;
1073         /* First, generate vertex index arrays for drawing with glDrawElements
1074          * Top and bottom are covered with a triangle fan
1075          * Each other stack with triangle strip. Only need to generate on
1076          * of those as we'll have to draw each stack separately, and can
1077          * just use different offsets in glDrawElements.
1078          */
1079
1080         /* Allocate buffers for indices, bail out if memory allocation fails */
1081         topIdx = malloc((slices+2)*sizeof(GLushort));
1082         bottomIdx = malloc((slices+2)*sizeof(GLushort));
1083         stripIdx = malloc((slices+1)*2*(stacks-2)*sizeof(GLushort));
1084         if (!(topIdx) || !(bottomIdx) || !(stripIdx))
1085         {
1086             free(topIdx);
1087             free(bottomIdx);
1088             free(stripIdx);
1089             fgError("Failed to allocate memory in fghGenerateSphere");
1090         }
1091
1092         /* TODO: Can do top and bottom as Triangle strip as well
1093            (just need to repeat top/btoom vertex a lot). Then we can draw
1094            the whole thing with just one index array and one for-looped call
1095            to glDrawElements.. That'll make it easier to reuse code with other
1096            Circular objects too
1097            */
1098         topIdx[0]=0;
1099         topIdx[1] = 1;                              /* repeat first slice's idx for closing off shape */
1100         for (j=slices, idx=2; j>0; j--, idx++)
1101             topIdx[idx] = j;
1102
1103         bottomIdx[0]=nVert-1;                       /* zero based index, last element in array... */
1104         for (j=0, idx=1; j<slices; j++, idx++)
1105             bottomIdx[idx] = nVert-(slices+1)+j;
1106         bottomIdx[idx] = nVert-(slices+1);          /* repeat first slice's idx for closing off shape */
1107
1108         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1109         for (i=0,idx=0; i<stacks-2; i++, idx+=2)
1110         {
1111             GLushort offset = 1+i*slices;             /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1112             for (j=0; j<slices; j++, idx+=2)
1113             {
1114                 stripIdx[idx  ] = offset+j+slices;
1115                 stripIdx[idx+1] = offset+j;
1116             }
1117             stripIdx[idx  ] = offset+slices;        /* repeat first slice's idx for closing off shape */
1118             stripIdx[idx+1] = offset+0;
1119         }
1120
1121
1122         /* draw */
1123         glEnableClientState(GL_VERTEX_ARRAY);
1124         glEnableClientState(GL_NORMAL_ARRAY);
1125
1126         glVertexPointer(3, GL_FLOAT, 0, vertices);
1127         glNormalPointer(GL_FLOAT, 0, normals);
1128         /*draw top*/
1129         glDrawElements(GL_TRIANGLE_FAN,slices+2,GL_UNSIGNED_SHORT,topIdx);
1130         /*draw stacks*/
1131         for (i=0; i<stacks-2; i++)
1132             glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1133         /*draw bottom*/
1134         glDrawElements(GL_TRIANGLE_FAN,slices+2,GL_UNSIGNED_SHORT,bottomIdx);
1135
1136         glDisableClientState(GL_VERTEX_ARRAY);
1137         glDisableClientState(GL_NORMAL_ARRAY);
1138
1139         /* cleanup allocated memory */
1140         free(topIdx);
1141         free(bottomIdx);
1142         free(stripIdx);
1143     }
1144     
1145     /* cleanup allocated memory */
1146     free(vertices);
1147     free(normals);
1148 }
1149
1150 #endif /* GL_ES_VERSION_2_0 */
1151
1152
1153 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
1154
1155
1156 /*
1157  * Draws a solid sphere
1158  */
1159 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
1160 {
1161     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
1162
1163     fghSphere( radius, slices, stacks, FALSE );
1164 }
1165
1166 /*
1167  * Draws a wire sphere
1168  */
1169 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
1170 {
1171     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
1172
1173     fghSphere( radius, slices, stacks, TRUE );
1174     
1175 }
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     const GLfloat cosn = ( (GLfloat)height / sqrtf( height * height + base * base ));
1196     const GLfloat sinn = ( (GLfloat)base   / sqrtf( height * height + base * base ));
1197
1198     /* Pre-computed circle */
1199
1200     GLfloat *sint,*cost;
1201
1202     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
1203
1204     fghCircleTable(&sint,&cost,-slices,FALSE);
1205
1206     /* Cover the circular base with a triangle fan... */
1207
1208     z0 = 0;
1209     z1 = zStep;
1210
1211     r0 = (GLfloat)base;
1212     r1 = r0 - rStep;
1213
1214     glBegin(GL_TRIANGLE_FAN);
1215
1216         glNormal3f(0,0,-1);
1217         glVertex3f(0,0, z0 );
1218
1219         for (j=0; j<=slices; j++)
1220             glVertex3f(cost[j]*r0, sint[j]*r0, z0);
1221
1222     glEnd();
1223
1224     /* Cover each stack with a triangle strip */
1225     for( i=0; i<stacks; i++ )
1226     {
1227         glBegin(GL_TRIANGLE_STRIP);
1228
1229             for(j=0; j<=slices; j++)
1230             {
1231                 glNormal3f(cost[j]*cosn, sint[j]*cosn, sinn);
1232                 glVertex3f(cost[j]*r0,   sint[j]*r0,   z0  );
1233                 glVertex3f(cost[j]*r1,   sint[j]*r1,   z1  );
1234             }
1235
1236             z0 = z1; z1 += zStep;
1237             r0 = r1; r1 -= rStep;
1238
1239         glEnd();
1240     }
1241
1242     /* Release sin and cos tables */
1243
1244     free(sint);
1245     free(cost);
1246 }
1247
1248 /*
1249  * Draws a wire cone
1250  */
1251 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
1252 {
1253     int i,j;
1254
1255     /* Step in z and radius as stacks are drawn. */
1256
1257     GLfloat z = 0;
1258     GLfloat r = (GLfloat)base;
1259
1260     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1261     const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
1262
1263     /* Scaling factors for vertex normals */
1264
1265     const GLfloat cosn = ( (GLfloat)height / sqrtf( height * height + base * base ));
1266     const GLfloat sinn = ( (GLfloat)base   / sqrtf( height * height + base * base ));
1267
1268     /* Pre-computed circle */
1269
1270     GLfloat *sint,*cost;
1271
1272     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1273
1274     fghCircleTable(&sint,&cost,-slices,FALSE);
1275
1276     /* Draw the stacks... */
1277
1278     for (i=0; i<stacks; i++)
1279     {
1280         glBegin(GL_LINE_LOOP);
1281
1282             for( j=0; j<slices; j++ )
1283             {
1284                 glNormal3f(cost[j]*sinn, sint[j]*sinn, cosn);
1285                 glVertex3f(cost[j]*r,    sint[j]*r,    z   );
1286             }
1287
1288         glEnd();
1289
1290         z += zStep;
1291         r -= rStep;
1292     }
1293
1294     /* Draw the slices */
1295
1296     r = (GLfloat)base;
1297
1298     glBegin(GL_LINES);
1299
1300         for (j=0; j<slices; j++)
1301         {
1302             glNormal3f(cost[j]*sinn, sint[j]*sinn,          cosn  );
1303             glVertex3f(cost[j]*r,    sint[j]*r,             0     );
1304             glVertex3f(0,            0,            (GLfloat)height);
1305         }
1306
1307     glEnd();
1308
1309     /* Release sin and cos tables */
1310
1311     free(sint);
1312     free(cost);
1313 }
1314
1315
1316 /*
1317  * Draws a solid cylinder
1318  */
1319 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
1320 {
1321     int i,j;
1322
1323     /* Step in z and radius as stacks are drawn. */
1324     GLfloat radf = (GLfloat)radius;
1325     GLfloat z0,z1;
1326     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1327
1328     /* Pre-computed circle */
1329
1330     GLfloat *sint,*cost;
1331
1332     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1333
1334     fghCircleTable(&sint,&cost,-slices,FALSE);
1335
1336     /* Cover the base and top */
1337
1338     glBegin(GL_TRIANGLE_FAN);
1339         glNormal3f(0, 0, -1 );
1340         glVertex3f(0, 0,  0 );
1341         for (j=0; j<=slices; j++)
1342           glVertex3f(cost[j]*radf, sint[j]*radf, 0);
1343     glEnd();
1344
1345     glBegin(GL_TRIANGLE_FAN);
1346         glNormal3f(0, 0,          1     );
1347         glVertex3f(0, 0, (GLfloat)height);
1348         for (j=slices; j>=0; j--)
1349           glVertex3f(cost[j]*radf, sint[j]*radf, (GLfloat)height);
1350     glEnd();
1351
1352     /* Do the stacks */
1353
1354     z0 = 0;
1355     z1 = zStep;
1356
1357     for (i=1; i<=stacks; i++)
1358     {
1359         if (i==stacks)
1360             z1 = (GLfloat)height;
1361
1362         glBegin(GL_TRIANGLE_STRIP);
1363             for (j=0; j<=slices; j++ )
1364             {
1365                 glNormal3f(cost[j],      sint[j],      0  );
1366                 glVertex3f(cost[j]*radf, sint[j]*radf, z0 );
1367                 glVertex3f(cost[j]*radf, sint[j]*radf, z1 );
1368             }
1369         glEnd();
1370
1371         z0 = z1; z1 += zStep;
1372     }
1373
1374     /* Release sin and cos tables */
1375
1376     free(sint);
1377     free(cost);
1378 }
1379
1380 /*
1381  * Draws a wire cylinder
1382  */
1383 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
1384 {
1385     int i,j;
1386
1387     /* Step in z and radius as stacks are drawn. */
1388     GLfloat radf = (GLfloat)radius;
1389           GLfloat z = 0;
1390     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1391
1392     /* Pre-computed circle */
1393
1394     GLfloat *sint,*cost;
1395
1396     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1397
1398     fghCircleTable(&sint,&cost,-slices,FALSE);
1399
1400     /* Draw the stacks... */
1401
1402     for (i=0; i<=stacks; i++)
1403     {
1404         if (i==stacks)
1405             z = (GLfloat)height;
1406
1407         glBegin(GL_LINE_LOOP);
1408
1409             for( j=0; j<slices; j++ )
1410             {
1411                 glNormal3f(cost[j],      sint[j],      0);
1412                 glVertex3f(cost[j]*radf, sint[j]*radf, z);
1413             }
1414
1415         glEnd();
1416
1417         z += zStep;
1418     }
1419
1420     /* Draw the slices */
1421
1422     glBegin(GL_LINES);
1423
1424         for (j=0; j<slices; j++)
1425         {
1426             glNormal3f(cost[j],      sint[j],               0     );
1427             glVertex3f(cost[j]*radf, sint[j]*radf,          0     );
1428             glVertex3f(cost[j]*radf, sint[j]*radf, (GLfloat)height);
1429         }
1430
1431     glEnd();
1432
1433     /* Release sin and cos tables */
1434
1435     free(sint);
1436     free(cost);
1437 }
1438
1439 /*
1440  * Draws a wire torus
1441  */
1442 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1443 {
1444   GLfloat  iradius = (float)dInnerRadius, oradius = (float)dOuterRadius;
1445   GLfloat phi, psi, dpsi, dphi;
1446   GLfloat *vertex, *normal;
1447   int    i, j;
1448   GLfloat spsi, cpsi, sphi, cphi ;
1449
1450   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1451
1452   if ( nSides < 1 ) nSides = 1;
1453   if ( nRings < 1 ) nRings = 1;
1454
1455   /* Allocate the vertices array */
1456   vertex = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1457   normal = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1458
1459   glPushMatrix();
1460
1461   dpsi =  2.0f * (GLfloat)M_PI / (GLfloat)(nRings) ;
1462   dphi = -2.0f * (GLfloat)M_PI / (GLfloat)(nSides) ;
1463   psi  = 0.0f;
1464
1465   for( j=0; j<nRings; j++ )
1466   {
1467     cpsi = cosf( psi ) ;
1468     spsi = sinf( psi ) ;
1469     phi = 0.0f;
1470
1471     for( i=0; i<nSides; i++ )
1472     {
1473       int offset = 3 * ( j * nSides + i ) ;
1474       cphi = cosf( phi ) ;
1475       sphi = sinf( phi ) ;
1476       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1477       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1478       *(vertex + offset + 2) =                    sphi * iradius  ;
1479       *(normal + offset + 0) = cpsi * cphi ;
1480       *(normal + offset + 1) = spsi * cphi ;
1481       *(normal + offset + 2) =        sphi ;
1482       phi += dphi;
1483     }
1484
1485     psi += dpsi;
1486   }
1487
1488   for( i=0; i<nSides; i++ )
1489   {
1490     glBegin( GL_LINE_LOOP );
1491
1492     for( j=0; j<nRings; j++ )
1493     {
1494       int offset = 3 * ( j * nSides + i ) ;
1495       glNormal3fv( normal + offset );
1496       glVertex3fv( vertex + offset );
1497     }
1498
1499     glEnd();
1500   }
1501
1502   for( j=0; j<nRings; j++ )
1503   {
1504     glBegin(GL_LINE_LOOP);
1505
1506     for( i=0; i<nSides; i++ )
1507     {
1508       int offset = 3 * ( j * nSides + i ) ;
1509       glNormal3fv( normal + offset );
1510       glVertex3fv( vertex + offset );
1511     }
1512
1513     glEnd();
1514   }
1515
1516   free ( vertex ) ;
1517   free ( normal ) ;
1518   glPopMatrix();
1519 }
1520
1521 /*
1522  * Draws a solid torus
1523  */
1524 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1525 {
1526   GLfloat  iradius = (float)dInnerRadius, oradius = (float)dOuterRadius;
1527   GLfloat phi, psi, dpsi, dphi;
1528   GLfloat *vertex, *normal;
1529   int    i, j;
1530   GLfloat spsi, cpsi, sphi, cphi ;
1531
1532   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1533
1534   if ( nSides < 1 ) nSides = 1;
1535   if ( nRings < 1 ) nRings = 1;
1536
1537   /* Increment the number of sides and rings to allow for one more point than surface */
1538   nSides ++ ;
1539   nRings ++ ;
1540
1541   /* Allocate the vertices array */
1542   vertex = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1543   normal = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1544
1545   glPushMatrix();
1546
1547   dpsi =  2.0f * (GLfloat)M_PI / (GLfloat)(nRings - 1) ;
1548   dphi = -2.0f * (GLfloat)M_PI / (GLfloat)(nSides - 1) ;
1549   psi  = 0.0f;
1550
1551   for( j=0; j<nRings; j++ )
1552   {
1553     cpsi = cosf( psi ) ;
1554     spsi = sinf( psi ) ;
1555     phi = 0.0f;
1556
1557     for( i=0; i<nSides; i++ )
1558     {
1559       int offset = 3 * ( j * nSides + i ) ;
1560       cphi = cosf( phi ) ;
1561       sphi = sinf( phi ) ;
1562       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1563       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1564       *(vertex + offset + 2) =                    sphi * iradius  ;
1565       *(normal + offset + 0) = cpsi * cphi ;
1566       *(normal + offset + 1) = spsi * cphi ;
1567       *(normal + offset + 2) =        sphi ;
1568       phi += dphi;
1569     }
1570
1571     psi += dpsi;
1572   }
1573
1574     glBegin( GL_QUADS );
1575   for( i=0; i<nSides-1; i++ )
1576   {
1577     for( j=0; j<nRings-1; j++ )
1578     {
1579       int offset = 3 * ( j * nSides + i ) ;
1580       glNormal3fv( normal + offset );
1581       glVertex3fv( vertex + offset );
1582       glNormal3fv( normal + offset + 3 );
1583       glVertex3fv( vertex + offset + 3 );
1584       glNormal3fv( normal + offset + 3 * nSides + 3 );
1585       glVertex3fv( vertex + offset + 3 * nSides + 3 );
1586       glNormal3fv( normal + offset + 3 * nSides );
1587       glVertex3fv( vertex + offset + 3 * nSides );
1588     }
1589   }
1590
1591   glEnd();
1592
1593   free ( vertex ) ;
1594   free ( normal ) ;
1595   glPopMatrix();
1596 }
1597 #endif /* EGL_VERSION_1_0 */
1598
1599
1600
1601 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1602 /* Macro to generate interface functions */
1603 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1604     void FGAPIENTRY glutWire##nameICaps( void )\
1605     {\
1606         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1607         fgh##nameICaps( TRUE );\
1608     }\
1609     void FGAPIENTRY glutSolid##nameICaps( void )\
1610     {\
1611         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1612         fgh##nameICaps( FALSE );\
1613     }
1614
1615 void FGAPIENTRY glutWireCube( double dSize )
1616 {
1617     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1618     fghCube( (GLfloat)dSize, TRUE );
1619 }
1620 void FGAPIENTRY glutSolidCube( double dSize )
1621 {
1622     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1623     fghCube( (GLfloat)dSize, FALSE );
1624 }
1625
1626 DECLARE_SHAPE_INTERFACE(Dodecahedron);
1627 DECLARE_SHAPE_INTERFACE(Icosahedron);
1628 DECLARE_SHAPE_INTERFACE(Octahedron);
1629 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron);
1630
1631 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
1632 {
1633     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1634     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, TRUE );
1635 }
1636 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
1637 {
1638     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1639     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, FALSE );
1640 }
1641
1642 DECLARE_SHAPE_INTERFACE(Tetrahedron);
1643
1644
1645 /*** END OF FILE ***/