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