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