066a0d35478d4a93ddb810937c0b59a6f8719f69
[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 /* declare for drawing using the different OpenGL versions here so we can
34    have a nice code order below */
35 #ifndef GL_ES_VERSION_2_0
36 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
37                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
38                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
39     );
40 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
41                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
42 #endif
43 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
44                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
45                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
46                                   GLint attribute_v_coord, GLint attribute_v_normal
47     );
48 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
49                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
50                                    GLint attribute_v_coord, GLint attribute_v_normal);
51 /* declare function for generating visualization of normals */
52 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
53                                            GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
54 static void fghDrawNormalVisualization();
55
56 /* Drawing geometry:
57  * Explanation of the functions has to be separate for the polyhedra and
58  * the non-polyhedra (objects with a circular cross-section).
59  * Polyhedra:
60  *   - We have only implemented the five platonic solids and the rhomboid
61  *     dodecahedron. If you need more types of polyhedra, please see
62  *     CPolyhedron in MRPT
63  *   - Solids are drawn by glDrawArrays if composed of triangular faces
64  *     (the tetrahedron, octahedron, and icosahedron), or are first
65  *     decomposed into triangles and then drawn by glDrawElements if its
66  *     faces are squares or pentagons (cube, dodecahedron and rhombic
67  *     dodecahedron) as some vertices are repeated in that case.
68  *   - WireFrame drawing is done using a GL_LINE_LOOP per face, and thus
69  *     issuing one draw call per face. glDrawArrays is always used as no
70  *     triangle decomposition is needed to draw faces. We use the "first"
71  *     parameter in glDrawArrays to go from face to face.
72  * 
73  * Non-polyhedra:
74  *   - We have implemented the sphere, cylinder, cone and torus.
75  *   - All shapes are characterized by two parameters: the number of
76  *     subdivisions along two axes used to construct the shape's vertices 
77  *     (e.g. stacks and slices for the sphere).
78  *     As different subdivisions are most suitable for different shapes,
79  *     and are thus also named differently, I wont provide general comments
80  *     on them here.
81  *   - Solids are drawn using glDrawArrays and GL_TRIANGLE_STRIP. Each
82  *     strip covers one revolution around one of the two subdivision axes
83  *     of the shape.
84  *   - WireFrame drawing is done for the subdivisions along the two axes 
85  *     separately, usually using GL_LINE_LOOP. Vertex index arrays are
86  *     built containing the vertices to be drawn for each loop, which are
87  *     then drawn using multiple calls to glDrawElements. As the number of
88  *     subdivisions along the two axes is not guaranteed to be equal, the
89  *     vertex indices for e.g. stacks and slices are stored in separate
90  *     arrays, which makes the input to the drawing function a bit clunky,
91  *     but allows for the same drawing function to be used for all shapes.
92  */
93
94
95 /**
96  * Draw geometric shape in wire mode (only edges)
97  *
98  * Arguments:
99  * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
100  *   The vertex coordinate and normal buffers, and the number of entries in
101  *   those
102  * GLushort *vertIdxs
103  *   a vertex indices buffer, optional (never passed for the polyhedra)
104  * GLsizei numParts, GLsizei numVertPerPart
105  *   polyhedra: number of faces, and the number of vertices for drawing
106  *     each face
107  *   non-polyhedra: number of edges to draw for first subdivision (not
108  *     necessarily equal to number of subdivisions requested by user, e.g.
109  *     as each subdivision is enclosed by two edges), and number of
110  *     vertices for drawing each
111  *   numParts * numVertPerPart gives the number of entries in the vertex
112  *     array vertIdxs
113  * GLenum vertexMode
114  *   vertex drawing mode (e.g. always GL_LINE_LOOP for polyhedra, varies
115  *   for others)
116  * GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
117  *   non-polyhedra only: same as the above, but now for subdivisions along
118  *   the other axis. Always drawn as GL_LINE_LOOP.
119  *
120  * Feel free to contribute better naming ;)
121  */
122 static void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
123                                 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
124                                 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
125     )
126 {
127     GLint attribute_v_coord  = fgStructure.CurrentWindow->Window.attribute_v_coord;
128     GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal;
129
130     if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
131         /* User requested a 2.0 draw */
132         fghDrawGeometryWire20(vertices, normals, numVertices,
133                               vertIdxs, numParts, numVertPerPart, vertexMode,
134                               vertIdxs2, numParts2, numVertPerPart2,
135                               attribute_v_coord, attribute_v_normal);
136 #ifndef GL_ES_VERSION_2_0
137     else
138         fghDrawGeometryWire11(vertices, normals,
139                               vertIdxs, numParts, numVertPerPart, vertexMode,
140                               vertIdxs2, numParts2, numVertPerPart2);
141 #endif
142 }
143
144 /* Draw the geometric shape with filled triangles
145  *
146  * Arguments:
147  * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
148  *   The vertex coordinate and normal buffers, and the number of entries in
149  *   those
150  * GLushort *vertIdxs
151  *   a vertex indices buffer, optional (not passed for the polyhedra with
152  *   triangular faces)
153  * GLsizei numParts, GLsizei numVertPerPart
154  *   polyhedra: not used for polyhedra with triangular faces
155        (numEdgePerFace==3), as each vertex+normal pair is drawn only once,
156        so no vertex indices are used.
157        Else, the shape was triangulated (DECOMPOSE_TO_TRIANGLE), leading to
158        reuse of some vertex+normal pairs, and thus the need to draw with
159        glDrawElements. numParts is always 1 in this case (we can draw the
160        whole object with one call to glDrawElements as the vertex index
161        array contains separate triangles), and numVertPerPart indicates
162        the number of vertex indices in the vertex array.
163  *   non-polyhedra: number of parts (GL_TRIANGLE_STRIPs) to be drawn
164        separately (numParts calls to glDrawElements) to create the object.
165        numVertPerPart indicates the number of vertex indices to be
166        processed at each draw call.
167  *   numParts * numVertPerPart gives the number of entries in the vertex
168  *     array vertIdxs
169  */
170 static void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
171                                  GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
172 {
173     GLint attribute_v_coord  = fgStructure.CurrentWindow->Window.attribute_v_coord;
174     GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal;
175
176     if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
177         /* User requested a 2.0 draw */
178         fghDrawGeometrySolid20(vertices, normals, numVertices,
179                                vertIdxs, numParts, numVertIdxsPerPart,
180                                attribute_v_coord, attribute_v_normal);
181 #ifndef GL_ES_VERSION_2_0
182     else
183     {
184         fghDrawGeometrySolid11(vertices, normals, numVertices,
185                                vertIdxs, numParts, numVertIdxsPerPart);
186
187         if (fgStructure.CurrentWindow->State.VisualizeNormals)
188         {
189             /* generate normals for each vertex to be drawn as well */
190             fghGenerateNormalVisualization(vertices, normals, numVertices,
191                                            vertIdxs, numParts, numVertIdxsPerPart);
192             /* draw normals for each vertex as well */
193             fghDrawNormalVisualization();
194         }
195     }
196 #endif
197 }
198
199
200
201 /* Version for OpenGL (ES) 1.1 */
202 #ifndef GL_ES_VERSION_2_0
203 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
204                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
205                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
206     )
207 {
208     int i;
209     
210     glEnableClientState(GL_VERTEX_ARRAY);
211     glEnableClientState(GL_NORMAL_ARRAY);
212
213     glVertexPointer(3, GL_FLOAT, 0, vertices);
214     glNormalPointer(GL_FLOAT, 0, normals);
215
216     
217     if (!vertIdxs)
218         /* Draw per face (TODO: could use glMultiDrawArrays if available) */
219         for (i=0; i<numParts; i++)
220             glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
221     else
222         for (i=0; i<numParts; i++)
223             glDrawElements(vertexMode,numVertPerPart,GL_UNSIGNED_SHORT,vertIdxs+i*numVertPerPart);
224
225     if (vertIdxs2)
226         for (i=0; i<numParts2; i++)
227             glDrawElements(GL_LINE_LOOP,numVertPerPart2,GL_UNSIGNED_SHORT,vertIdxs2+i*numVertPerPart2);
228
229     glDisableClientState(GL_VERTEX_ARRAY);
230     glDisableClientState(GL_NORMAL_ARRAY);
231 }
232
233
234 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
235                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
236 {
237     int i;
238
239     glEnableClientState(GL_VERTEX_ARRAY);
240     glEnableClientState(GL_NORMAL_ARRAY);
241
242     glVertexPointer(3, GL_FLOAT, 0, vertices);
243     glNormalPointer(GL_FLOAT, 0, normals);
244
245     if (!vertIdxs)
246         glDrawArrays(GL_TRIANGLES, 0, numVertices);
247     else
248         if (numParts>1)
249             for (i=0; i<numParts; i++)
250                 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs+i*numVertIdxsPerPart);
251         else
252             glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs);
253
254     glDisableClientState(GL_VERTEX_ARRAY);
255     glDisableClientState(GL_NORMAL_ARRAY);
256 }
257 #endif
258
259 /* Version for OpenGL (ES) >= 2.0 */
260 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
261                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
262                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
263                                   GLint attribute_v_coord, GLint attribute_v_normal)
264 {
265     GLuint vbo_coords = 0, vbo_normals = 0,
266         ibo_elements = 0, ibo_elements2 = 0;
267     GLsizei numVertIdxs = numParts * numVertPerPart;
268     GLsizei numVertIdxs2 = numParts2 * numVertPerPart2;
269     int i;
270
271     if (numVertices > 0 && attribute_v_coord != -1) {
272         fghGenBuffers(1, &vbo_coords);
273         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
274         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
275                       vertices, FGH_STATIC_DRAW);
276     }
277     
278     if (numVertices > 0 && attribute_v_normal != -1) {
279         fghGenBuffers(1, &vbo_normals);
280         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
281         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
282                       normals, FGH_STATIC_DRAW);
283     }
284     
285     if (vertIdxs != NULL) {
286         fghGenBuffers(1, &ibo_elements);
287         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
288         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
289                       vertIdxs, FGH_STATIC_DRAW);
290         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
291     }
292
293     if (vertIdxs2 != NULL) {
294         fghGenBuffers(1, &ibo_elements2);
295         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
296         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs2 * sizeof(vertIdxs2[0]),
297                       vertIdxs2, FGH_STATIC_DRAW);
298         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
299     }
300
301     if (vbo_coords) {
302         fghEnableVertexAttribArray(attribute_v_coord);
303         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
304         fghVertexAttribPointer(
305             attribute_v_coord,  /* attribute */
306             3,                  /* number of elements per vertex, here (x,y,z) */
307             GL_FLOAT,           /* the type of each element */
308             GL_FALSE,           /* take our values as-is */
309             0,                  /* no extra data between each position */
310             0                   /* offset of first element */
311         );
312         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
313     }
314
315     if (vbo_normals) {
316         fghEnableVertexAttribArray(attribute_v_normal);
317         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
318         fghVertexAttribPointer(
319             attribute_v_normal, /* attribute */
320             3,                  /* number of elements per vertex, here (x,y,z) */
321             GL_FLOAT,           /* the type of each element */
322             GL_FALSE,           /* take our values as-is */
323             0,                  /* no extra data between each position */
324             0                   /* offset of first element */
325         );
326         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
327     }
328
329     if (!vertIdxs) {
330         /* Draw per face (TODO: could use glMultiDrawArrays if available) */
331         for (i=0; i<numParts; i++)
332             glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
333     } else {
334         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
335         for (i=0; i<numParts; i++)
336             glDrawElements(vertexMode, numVertPerPart,
337                            GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertPerPart));
338         /* Clean existing bindings before clean-up */
339         /* Android showed instability otherwise */
340         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
341     }
342
343     if (vertIdxs2) {
344         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
345         for (i=0; i<numParts2; i++)
346             glDrawElements(GL_LINE_LOOP, numVertPerPart2,
347                            GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs2[0])*i*numVertPerPart2));
348         /* Clean existing bindings before clean-up */
349         /* Android showed instability otherwise */
350         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
351     }
352     
353     if (vbo_coords != 0)
354         fghDisableVertexAttribArray(attribute_v_coord);
355     if (vbo_normals != 0)
356         fghDisableVertexAttribArray(attribute_v_normal);
357     
358     if (vbo_coords != 0)
359         fghDeleteBuffers(1, &vbo_coords);
360     if (vbo_normals != 0)
361         fghDeleteBuffers(1, &vbo_normals);
362     if (ibo_elements != 0)
363         fghDeleteBuffers(1, &ibo_elements);
364     if (ibo_elements2 != 0)
365         fghDeleteBuffers(1, &ibo_elements2);
366 }
367
368
369
370
371 /* Version for OpenGL (ES) >= 2.0 */
372 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
373                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
374                                    GLint attribute_v_coord, GLint attribute_v_normal)
375 {
376     GLuint vbo_coords = 0, vbo_normals = 0, ibo_elements = 0;
377     GLsizei numVertIdxs = numParts * numVertIdxsPerPart;
378     int i;
379   
380     if (numVertices > 0 && attribute_v_coord != -1) {
381         fghGenBuffers(1, &vbo_coords);
382         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
383         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
384                       vertices, FGH_STATIC_DRAW);
385         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
386     }
387     
388     if (numVertices > 0 && attribute_v_normal != -1) {
389         fghGenBuffers(1, &vbo_normals);
390         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
391         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
392                       normals, FGH_STATIC_DRAW);
393         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
394     }
395     
396     if (vertIdxs != NULL) {
397         fghGenBuffers(1, &ibo_elements);
398         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
399         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
400                       vertIdxs, FGH_STATIC_DRAW);
401         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
402     }
403     
404     if (vbo_coords) {
405         fghEnableVertexAttribArray(attribute_v_coord);
406         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
407         fghVertexAttribPointer(
408             attribute_v_coord,  /* attribute */
409             3,                  /* number of elements per vertex, here (x,y,z) */
410             GL_FLOAT,           /* the type of each element */
411             GL_FALSE,           /* take our values as-is */
412             0,                  /* no extra data between each position */
413             0                   /* offset of first element */
414         );
415         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
416     };
417     
418     if (vbo_normals) {
419         fghEnableVertexAttribArray(attribute_v_normal);
420         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
421         fghVertexAttribPointer(
422             attribute_v_normal, /* attribute */
423             3,                  /* number of elements per vertex, here (x,y,z) */
424             GL_FLOAT,           /* the type of each element */
425             GL_FALSE,           /* take our values as-is */
426             0,                  /* no extra data between each position */
427             0                   /* offset of first element */
428         );
429         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
430     };
431     
432     if (vertIdxs == NULL) {
433         glDrawArrays(GL_TRIANGLES, 0, numVertices);
434     } else {
435         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
436         if (numParts>1) {
437             for (i=0; i<numParts; i++) {
438                 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertIdxsPerPart));
439             }
440         } else {
441             glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, 0);
442         }
443         /* Clean existing bindings before clean-up */
444         /* Android showed instability otherwise */
445         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
446     }
447     
448     if (vbo_coords != 0)
449         fghDisableVertexAttribArray(attribute_v_coord);
450     if (vbo_normals != 0)
451         fghDisableVertexAttribArray(attribute_v_normal);
452     
453     if (vbo_coords != 0)
454         fghDeleteBuffers(1, &vbo_coords);
455     if (vbo_normals != 0)
456         fghDeleteBuffers(1, &vbo_normals);
457     if (ibo_elements != 0)
458         fghDeleteBuffers(1, &ibo_elements);
459 }
460
461
462
463 /**
464  * Generate vertex indices for visualizing the normals.
465  */
466 static GLfloat *verticesForNormalVisualization;
467 static GLushort numNormalVertices = 0;
468 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
469                                            GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
470 {
471     GLushort i,j;
472     /* calc number of vertices to generate, allocate. TODO: FREE again after draw!
473      * two for each vertex in the input shape
474      */
475     if (!vertIdxs)
476         numNormalVertices = numVertices * 2;
477     else
478         numNormalVertices = numParts * numVertIdxsPerPart * 2;
479     verticesForNormalVisualization = malloc(numNormalVertices*3 * sizeof(GLfloat));
480
481     /* Now generate vertices for lines to draw the normals */
482     if (!vertIdxs)
483     {
484         for (i=0,j=0; i<numNormalVertices*3/2; i+=3, j+=6)
485         {
486             verticesForNormalVisualization[j+0] = vertices[i+0];
487             verticesForNormalVisualization[j+1] = vertices[i+1];
488             verticesForNormalVisualization[j+2] = vertices[i+2];
489             verticesForNormalVisualization[j+3] = vertices[i+0] + normals[i+0]/4.f;
490             verticesForNormalVisualization[j+4] = vertices[i+1] + normals[i+1]/4.f;
491             verticesForNormalVisualization[j+5] = vertices[i+2] + normals[i+2]/4.f;
492         }
493     }
494     else
495     {
496         for (i=0,j=0; i<numNormalVertices/2; i++, j+=6)
497         {
498             GLushort idx = vertIdxs[i]*3;
499             verticesForNormalVisualization[j+0] = vertices[idx+0];
500             verticesForNormalVisualization[j+1] = vertices[idx+1];
501             verticesForNormalVisualization[j+2] = vertices[idx+2];
502             verticesForNormalVisualization[j+3] = vertices[idx+0] + normals[idx+0]/4.f;
503             verticesForNormalVisualization[j+4] = vertices[idx+1] + normals[idx+1]/4.f;
504             verticesForNormalVisualization[j+5] = vertices[idx+2] + normals[idx+2]/4.f;
505         }
506     }
507 }
508
509 static void fghDrawNormalVisualization()
510 {
511     GLfloat currentColor[4];
512     /* Setup draw color: (1,1,1)-shape's color */
513     glGetFloatv(GL_CURRENT_COLOR,currentColor);
514     glColor4f(1-currentColor[0],1-currentColor[1],1-currentColor[2],currentColor[3]);
515
516     glEnableClientState(GL_VERTEX_ARRAY);
517
518     glVertexPointer(3, GL_FLOAT, 0, verticesForNormalVisualization);
519     glDrawArrays(GL_LINES, 0, numNormalVertices);
520
521     glDisableClientState(GL_VERTEX_ARRAY);
522
523     /* Done, free memory, reset color */
524     free(verticesForNormalVisualization);
525     glColor4fv(currentColor);
526 }
527
528 /**
529  * Generate all combinations of vertices and normals needed to draw object.
530  * Optional shape decomposition to triangles:
531  * We'll use glDrawElements to draw all shapes that are not naturally
532  * composed of triangles, so generate an index vector here, using the
533  * below sampling scheme.
534  * Be careful to keep winding of all triangles counter-clockwise,
535  * assuming that input has correct winding...
536  */
537 static GLubyte   vert4Decomp[6] = {0,1,2, 0,2,3};             /* quad    : 4 input vertices, 6 output (2 triangles) */
538 static GLubyte   vert5Decomp[9] = {0,1,2, 0,2,4, 4,2,3};      /* pentagon: 5 input vertices, 9 output (3 triangles) */
539
540 static void fghGenerateGeometryWithIndexArray(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut, GLushort *vertIdxOut)
541 {
542     int i,j,numEdgeIdxPerFace;
543     GLubyte   *vertSamps = NULL;
544     switch (numEdgePerFace)
545     {
546     case 3:
547         /* nothing to do here, we'll draw with glDrawArrays */
548         break;
549     case 4:
550         vertSamps = vert4Decomp;
551         numEdgeIdxPerFace = 6;      /* 6 output vertices for each face */
552         break;
553     case 5:
554         vertSamps = vert5Decomp;
555         numEdgeIdxPerFace = 9;      /* 9 output vertices for each face */
556         break;
557     }
558     /*
559      * Build array with vertices using vertex coordinates and vertex indices
560      * Do same for normals.
561      * Need to do this because of different normals at shared vertices.
562      */
563     for (i=0; i<numFaces; i++)
564     {
565         int normIdx         = i*3;
566         int faceIdxVertIdx  = i*numEdgePerFace; /* index to first element of "row" in vertex indices */
567         for (j=0; j<numEdgePerFace; j++)
568         {
569             int outIdx  = i*numEdgePerFace*3+j*3;
570             int vertIdx = vertIndices[faceIdxVertIdx+j]*3;
571
572             vertOut[outIdx  ] = vertices[vertIdx  ];
573             vertOut[outIdx+1] = vertices[vertIdx+1];
574             vertOut[outIdx+2] = vertices[vertIdx+2];
575
576             normOut[outIdx  ] = normals [normIdx  ];
577             normOut[outIdx+1] = normals [normIdx+1];
578             normOut[outIdx+2] = normals [normIdx+2];
579         }
580
581         /* generate vertex indices for each face */
582         if (vertSamps)
583             for (j=0; j<numEdgeIdxPerFace; j++)
584                 vertIdxOut[i*numEdgeIdxPerFace+j] = faceIdxVertIdx + vertSamps[j];
585     }
586 }
587
588 static void fghGenerateGeometry(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut)
589 {
590     /* This function does the same as fghGenerateGeometryWithIndexArray, just skipping the index array generation... */
591     fghGenerateGeometryWithIndexArray(numFaces, numEdgePerFace, vertices, vertIndices, normals, vertOut, normOut, NULL);
592 }
593
594
595 /* -- INTERNAL SETUP OF GEOMETRY --------------------------------------- */
596 /* -- stuff that can be cached -- */
597 /* Cache of input to glDrawArrays or glDrawElements
598  * In general, we build arrays with all vertices or normals.
599  * We cant compress this and use glDrawElements as all combinations of
600  * vertices and normals are unique.
601  */
602 #define DECLARE_SHAPE_CACHE(name,nameICaps,nameCaps)\
603     static GLboolean name##Cached = FALSE;\
604     static GLfloat   name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
605     static GLfloat   name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
606     static void fgh##nameICaps##Generate()\
607     {\
608         fghGenerateGeometry(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
609                             name##_v, name##_vi, name##_n,\
610                             name##_verts, name##_norms);\
611     }
612 #define DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(name,nameICaps,nameCaps)\
613     static GLboolean name##Cached = FALSE;\
614     static GLfloat   name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
615     static GLfloat   name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
616     static GLushort  name##_vertIdxs[nameCaps##_VERT_PER_OBJ_TRI];\
617     static void fgh##nameICaps##Generate()\
618     {\
619         fghGenerateGeometryWithIndexArray(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
620                                           name##_v, name##_vi, name##_n,\
621                                           name##_verts, name##_norms, name##_vertIdxs);\
622     }
623
624 /* -- Cube -- */
625 #define CUBE_NUM_VERT           8
626 #define CUBE_NUM_FACES          6
627 #define CUBE_NUM_EDGE_PER_FACE  4
628 #define CUBE_VERT_PER_OBJ       (CUBE_NUM_FACES*CUBE_NUM_EDGE_PER_FACE)
629 #define CUBE_VERT_ELEM_PER_OBJ  (CUBE_VERT_PER_OBJ*3)
630 #define CUBE_VERT_PER_OBJ_TRI   (CUBE_VERT_PER_OBJ+CUBE_NUM_FACES*2)    /* 2 extra edges per face when drawing quads as triangles */
631 /* Vertex Coordinates */
632 static GLfloat cube_v[CUBE_NUM_VERT*3] =
633 {
634      .5f, .5f, .5f,
635     -.5f, .5f, .5f,
636     -.5f,-.5f, .5f,
637      .5f,-.5f, .5f,
638      .5f,-.5f,-.5f,
639      .5f, .5f,-.5f,
640     -.5f, .5f,-.5f,
641     -.5f,-.5f,-.5f
642 };
643 /* Normal Vectors */
644 static GLfloat cube_n[CUBE_NUM_FACES*3] =
645 {
646      0.0f, 0.0f, 1.0f,
647      1.0f, 0.0f, 0.0f,
648      0.0f, 1.0f, 0.0f,
649     -1.0f, 0.0f, 0.0f,
650      0.0f,-1.0f, 0.0f,
651      0.0f, 0.0f,-1.0f
652 };
653
654 /* Vertex indices, as quads, before triangulation */
655 static GLubyte cube_vi[CUBE_VERT_PER_OBJ] =
656 {
657     0,1,2,3,
658     0,3,4,5,
659     0,5,6,1,
660     1,6,7,2,
661     7,4,3,2,
662     4,7,6,5
663 };
664 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(cube,Cube,CUBE)
665
666 /* -- Dodecahedron -- */
667 /* Magic Numbers:  It is possible to create a dodecahedron by attaching two
668  * pentagons to each face of of a cube. The coordinates of the points are:
669  *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
670  * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
671  *       x = 0.61803398875 and z = 1.61803398875.
672  */
673 #define DODECAHEDRON_NUM_VERT           20
674 #define DODECAHEDRON_NUM_FACES          12
675 #define DODECAHEDRON_NUM_EDGE_PER_FACE  5
676 #define DODECAHEDRON_VERT_PER_OBJ       (DODECAHEDRON_NUM_FACES*DODECAHEDRON_NUM_EDGE_PER_FACE)
677 #define DODECAHEDRON_VERT_ELEM_PER_OBJ  (DODECAHEDRON_VERT_PER_OBJ*3)
678 #define DODECAHEDRON_VERT_PER_OBJ_TRI   (DODECAHEDRON_VERT_PER_OBJ+DODECAHEDRON_NUM_FACES*4)    /* 4 extra edges per face when drawing pentagons as triangles */
679 /* Vertex Coordinates */
680 static GLfloat dodecahedron_v[DODECAHEDRON_NUM_VERT*3] =
681 {
682                0.0f,  1.61803398875f,  0.61803398875f,
683     -          1.0f,            1.0f,            1.0f,
684     -0.61803398875f,            0.0f,  1.61803398875f,
685      0.61803398875f,            0.0f,  1.61803398875f,
686                1.0f,            1.0f,            1.0f,
687                0.0f,  1.61803398875f, -0.61803398875f,
688                1.0f,            1.0f, -          1.0f,
689      0.61803398875f,            0.0f, -1.61803398875f,
690     -0.61803398875f,            0.0f, -1.61803398875f,
691     -          1.0f,            1.0f, -          1.0f,
692                0.0f, -1.61803398875f,  0.61803398875f,
693                1.0f, -          1.0f,            1.0f,
694     -          1.0f, -          1.0f,            1.0f,
695                0.0f, -1.61803398875f, -0.61803398875f,
696     -          1.0f, -          1.0f, -          1.0f,
697                1.0f, -          1.0f, -          1.0f,
698      1.61803398875f, -0.61803398875f,            0.0f,
699      1.61803398875f,  0.61803398875f,            0.0f,
700     -1.61803398875f,  0.61803398875f,            0.0f,
701     -1.61803398875f, -0.61803398875f,            0.0f
702 };
703 /* Normal Vectors */
704 static GLfloat dodecahedron_n[DODECAHEDRON_NUM_FACES*3] =
705 {
706                 0.0f,  0.525731112119f,  0.850650808354f,
707                 0.0f,  0.525731112119f, -0.850650808354f,
708                 0.0f, -0.525731112119f,  0.850650808354f,
709                 0.0f, -0.525731112119f, -0.850650808354f,
710
711      0.850650808354f,             0.0f,  0.525731112119f,
712     -0.850650808354f,             0.0f,  0.525731112119f,
713      0.850650808354f,             0.0f, -0.525731112119f,
714     -0.850650808354f,             0.0f, -0.525731112119f,
715
716      0.525731112119f,  0.850650808354f,             0.0f,
717      0.525731112119f, -0.850650808354f,             0.0f,
718     -0.525731112119f,  0.850650808354f,             0.0f, 
719     -0.525731112119f, -0.850650808354f,             0.0f,
720 };
721
722 /* Vertex indices */
723 static GLubyte dodecahedron_vi[DODECAHEDRON_VERT_PER_OBJ] =
724 {
725      0,  1,  2,  3,  4, 
726      5,  6,  7,  8,  9, 
727     10, 11,  3,  2, 12, 
728     13, 14,  8,  7, 15, 
729
730      3, 11, 16, 17,  4, 
731      2,  1, 18, 19, 12, 
732      7,  6, 17, 16, 15, 
733      8, 14, 19, 18,  9, 
734
735     17,  6,  5,  0,  4, 
736     16, 11, 10, 13, 15, 
737     18,  1,  0,  5,  9, 
738     19, 14, 13, 10, 12
739 };
740 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
741
742
743 /* -- Icosahedron -- */
744 #define ICOSAHEDRON_NUM_VERT           12
745 #define ICOSAHEDRON_NUM_FACES          20
746 #define ICOSAHEDRON_NUM_EDGE_PER_FACE  3
747 #define ICOSAHEDRON_VERT_PER_OBJ       (ICOSAHEDRON_NUM_FACES*ICOSAHEDRON_NUM_EDGE_PER_FACE)
748 #define ICOSAHEDRON_VERT_ELEM_PER_OBJ  (ICOSAHEDRON_VERT_PER_OBJ*3)
749 #define ICOSAHEDRON_VERT_PER_OBJ_TRI   ICOSAHEDRON_VERT_PER_OBJ
750 /* Vertex Coordinates */
751 static GLfloat icosahedron_v[ICOSAHEDRON_NUM_VERT*3] =
752 {
753                 1.0f,             0.0f,             0.0f,
754      0.447213595500f,  0.894427191000f,             0.0f,
755      0.447213595500f,  0.276393202252f,  0.850650808354f,
756      0.447213595500f, -0.723606797748f,  0.525731112119f,
757      0.447213595500f, -0.723606797748f, -0.525731112119f,
758      0.447213595500f,  0.276393202252f, -0.850650808354f,
759     -0.447213595500f, -0.894427191000f,             0.0f,
760     -0.447213595500f, -0.276393202252f,  0.850650808354f,
761     -0.447213595500f,  0.723606797748f,  0.525731112119f,
762     -0.447213595500f,  0.723606797748f, -0.525731112119f,
763     -0.447213595500f, -0.276393202252f, -0.850650808354f,
764     -           1.0f,             0.0f,             0.0f
765 };
766 /* Normal Vectors:
767  * 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] ) ;
768  * 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] ) ;
769  * 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] ) ;
770 */
771 static GLfloat icosahedron_n[ICOSAHEDRON_NUM_FACES*3] =
772 {
773      0.760845213037948f,  0.470228201835026f,  0.341640786498800f,
774      0.760845213036861f, -0.179611190632978f,  0.552786404500000f,
775      0.760845213033849f, -0.581234022404097f,                0.0f,
776      0.760845213036861f, -0.179611190632978f, -0.552786404500000f,
777      0.760845213037948f,  0.470228201835026f, -0.341640786498800f,
778      0.179611190628666f,  0.760845213037948f,  0.552786404498399f,
779      0.179611190634277f, -0.290617011204044f,  0.894427191000000f,
780      0.179611190633958f, -0.940456403667806f,                0.0f,
781      0.179611190634278f, -0.290617011204044f, -0.894427191000000f,
782      0.179611190628666f,  0.760845213037948f, -0.552786404498399f,
783     -0.179611190633958f,  0.940456403667806f,                0.0f,
784     -0.179611190634277f,  0.290617011204044f,  0.894427191000000f,
785     -0.179611190628666f, -0.760845213037948f,  0.552786404498399f,
786     -0.179611190628666f, -0.760845213037948f, -0.552786404498399f,
787     -0.179611190634277f,  0.290617011204044f, -0.894427191000000f,
788     -0.760845213036861f,  0.179611190632978f, -0.552786404500000f,
789     -0.760845213033849f,  0.581234022404097f,                0.0f,
790     -0.760845213036861f,  0.179611190632978f,  0.552786404500000f,
791     -0.760845213037948f, -0.470228201835026f,  0.341640786498800f,
792     -0.760845213037948f, -0.470228201835026f, -0.341640786498800f,
793 };
794
795 /* Vertex indices */
796 static GLubyte icosahedron_vi[ICOSAHEDRON_VERT_PER_OBJ] =
797 {
798     0,   1,  2 ,
799     0,   2,  3 ,
800     0,   3,  4 ,
801     0,   4,  5 ,
802     0,   5,  1 ,
803     1,   8,  2 ,
804     2,   7,  3 ,
805     3,   6,  4 ,
806     4,  10,  5 ,
807     5,   9,  1 ,
808     1,   9,  8 ,
809     2,   8,  7 ,
810     3,   7,  6 ,
811     4,   6, 10 ,
812     5,  10,  9 ,
813     11,  9, 10 ,
814     11,  8,  9 ,
815     11,  7,  8 ,
816     11,  6,  7 ,
817     11, 10,  6 
818 };
819 DECLARE_SHAPE_CACHE(icosahedron,Icosahedron,ICOSAHEDRON)
820
821 /* -- Octahedron -- */
822 #define OCTAHEDRON_NUM_VERT           6
823 #define OCTAHEDRON_NUM_FACES          8
824 #define OCTAHEDRON_NUM_EDGE_PER_FACE  3
825 #define OCTAHEDRON_VERT_PER_OBJ       (OCTAHEDRON_NUM_FACES*OCTAHEDRON_NUM_EDGE_PER_FACE)
826 #define OCTAHEDRON_VERT_ELEM_PER_OBJ  (OCTAHEDRON_VERT_PER_OBJ*3)
827 #define OCTAHEDRON_VERT_PER_OBJ_TRI   OCTAHEDRON_VERT_PER_OBJ
828
829 /* Vertex Coordinates */
830 static GLfloat octahedron_v[OCTAHEDRON_NUM_VERT*3] =
831 {
832      1.f,  0.f,  0.f,
833      0.f,  1.f,  0.f,
834      0.f,  0.f,  1.f,
835     -1.f,  0.f,  0.f,
836      0.f, -1.f,  0.f,
837      0.f,  0.f, -1.f,
838
839 };
840 /* Normal Vectors */
841 static GLfloat octahedron_n[OCTAHEDRON_NUM_FACES*3] =
842 {
843      0.577350269189f, 0.577350269189f, 0.577350269189f,    /* sqrt(1/3) */
844      0.577350269189f, 0.577350269189f,-0.577350269189f,
845      0.577350269189f,-0.577350269189f, 0.577350269189f,
846      0.577350269189f,-0.577350269189f,-0.577350269189f,
847     -0.577350269189f, 0.577350269189f, 0.577350269189f,
848     -0.577350269189f, 0.577350269189f,-0.577350269189f,
849     -0.577350269189f,-0.577350269189f, 0.577350269189f,
850     -0.577350269189f,-0.577350269189f,-0.577350269189f
851
852 };
853
854 /* Vertex indices */
855 static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OBJ] =
856 {
857     0, 1, 2,
858     0, 5, 1,
859     0, 2, 4,
860     0, 4, 5,
861     3, 2, 1,
862     3, 1, 5,
863     3, 4, 2,
864     3, 5, 4
865 };
866 DECLARE_SHAPE_CACHE(octahedron,Octahedron,OCTAHEDRON)
867
868 /* -- RhombicDodecahedron -- */
869 #define RHOMBICDODECAHEDRON_NUM_VERT            14
870 #define RHOMBICDODECAHEDRON_NUM_FACES           12
871 #define RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE   4
872 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ       (RHOMBICDODECAHEDRON_NUM_FACES*RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE)
873 #define RHOMBICDODECAHEDRON_VERT_ELEM_PER_OBJ  (RHOMBICDODECAHEDRON_VERT_PER_OBJ*3)
874 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ_TRI   (RHOMBICDODECAHEDRON_VERT_PER_OBJ+RHOMBICDODECAHEDRON_NUM_FACES*2)    /* 2 extra edges per face when drawing quads as triangles */
875
876 /* Vertex Coordinates */
877 static GLfloat rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] =
878 {
879                 0.0f,             0.0f,  1.0f,
880      0.707106781187f,             0.0f,  0.5f,
881                 0.0f,  0.707106781187f,  0.5f,
882     -0.707106781187f,             0.0f,  0.5f,
883                 0.0f, -0.707106781187f,  0.5f,
884      0.707106781187f,  0.707106781187f,  0.0f,
885     -0.707106781187f,  0.707106781187f,  0.0f,
886     -0.707106781187f, -0.707106781187f,  0.0f,
887      0.707106781187f, -0.707106781187f,  0.0f,
888      0.707106781187f,             0.0f, -0.5f,
889                 0.0f,  0.707106781187f, -0.5f,
890     -0.707106781187f,             0.0f, -0.5f,
891                 0.0f, -0.707106781187f, -0.5f,
892                 0.0f,             0.0f, -1.0f
893 };
894 /* Normal Vectors */
895 static GLfloat rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] =
896 {
897      0.353553390594f,  0.353553390594f,  0.5f,
898     -0.353553390594f,  0.353553390594f,  0.5f,
899     -0.353553390594f, -0.353553390594f,  0.5f,
900      0.353553390594f, -0.353553390594f,  0.5f,
901                 0.0f,             1.0f,  0.0f,
902     -           1.0f,             0.0f,  0.0f,
903                 0.0f, -           1.0f,  0.0f,
904                 1.0f,             0.0f,  0.0f,
905      0.353553390594f,  0.353553390594f, -0.5f,
906     -0.353553390594f,  0.353553390594f, -0.5f,
907     -0.353553390594f, -0.353553390594f, -0.5f,
908      0.353553390594f, -0.353553390594f, -0.5f
909 };
910
911 /* Vertex indices */
912 static GLubyte rhombicdodecahedron_vi[RHOMBICDODECAHEDRON_VERT_PER_OBJ] =
913 {
914     0,  1,  5,  2,
915     0,  2,  6,  3,
916     0,  3,  7,  4,
917     0,  4,  8,  1,
918     5, 10,  6,  2,
919     6, 11,  7,  3,
920     7, 12,  8,  4,
921     8,  9,  5,  1,
922     5,  9, 13, 10,
923     6, 10, 13, 11,
924     7, 11, 13, 12,
925     8, 12, 13,  9
926 };
927 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
928
929 /* -- Tetrahedron -- */
930 /* Magic Numbers:  r0 = ( 1, 0, 0 )
931  *                 r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
932  *                 r2 = ( -1/3, - sqrt(2) / 3,  sqrt(6) / 3 )
933  *                 r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
934  * |r0| = |r1| = |r2| = |r3| = 1
935  * Distance between any two points is 2 sqrt(6) / 3
936  *
937  * Normals:  The unit normals are simply the negative of the coordinates of the point not on the surface.
938  */
939 #define TETRAHEDRON_NUM_VERT            4
940 #define TETRAHEDRON_NUM_FACES           4
941 #define TETRAHEDRON_NUM_EDGE_PER_FACE   3
942 #define TETRAHEDRON_VERT_PER_OBJ        (TETRAHEDRON_NUM_FACES*TETRAHEDRON_NUM_EDGE_PER_FACE)
943 #define TETRAHEDRON_VERT_ELEM_PER_OBJ   (TETRAHEDRON_VERT_PER_OBJ*3)
944 #define TETRAHEDRON_VERT_PER_OBJ_TRI    TETRAHEDRON_VERT_PER_OBJ
945
946 /* Vertex Coordinates */
947 static GLfloat tetrahedron_v[TETRAHEDRON_NUM_VERT*3] =
948 {
949                 1.0f,             0.0f,             0.0f,
950     -0.333333333333f,  0.942809041582f,             0.0f,
951     -0.333333333333f, -0.471404520791f,  0.816496580928f,
952     -0.333333333333f, -0.471404520791f, -0.816496580928f
953 };
954 /* Normal Vectors */
955 static GLfloat tetrahedron_n[TETRAHEDRON_NUM_FACES*3] =
956 {
957     -           1.0f,             0.0f,             0.0f,
958      0.333333333333f, -0.942809041582f,             0.0f,
959      0.333333333333f,  0.471404520791f, -0.816496580928f,
960      0.333333333333f,  0.471404520791f,  0.816496580928f
961 };
962
963 /* Vertex indices */
964 static GLubyte tetrahedron_vi[TETRAHEDRON_VERT_PER_OBJ] =
965 {
966     1, 3, 2,
967     0, 2, 3,
968     0, 3, 1,
969     0, 1, 2
970 };
971 DECLARE_SHAPE_CACHE(tetrahedron,Tetrahedron,TETRAHEDRON)
972
973 /* -- Sierpinski Sponge -- */
974 static unsigned int ipow (int x, unsigned int y)
975 {
976     /* return y==0? 1: y==1? x: (y%2? x: 1) * ipow(x*x, y/2); */
977     if (y==0)
978         return 1;
979     else
980     {
981         if (y==1)
982             return x;
983         else
984         {
985             return (y%2? x: 1) * ipow(x*x, y/2);
986         }
987     }
988 }
989
990 static void fghSierpinskiSpongeGenerate ( int numLevels, double offset[3], GLfloat scale, GLfloat* vertices, GLfloat* normals )
991 {
992     int i, j;
993     if ( numLevels == 0 )
994     {
995         for (i=0; i<TETRAHEDRON_NUM_FACES; i++)
996         {
997             int normIdx         = i*3;
998             int faceIdxVertIdx  = i*TETRAHEDRON_NUM_EDGE_PER_FACE;
999             for (j=0; j<TETRAHEDRON_NUM_EDGE_PER_FACE; j++)
1000             {
1001                 int outIdx  = i*TETRAHEDRON_NUM_EDGE_PER_FACE*3+j*3;
1002                 int vertIdx = tetrahedron_vi[faceIdxVertIdx+j]*3;
1003
1004                 vertices[outIdx  ] = (GLfloat)offset[0] + scale * tetrahedron_v[vertIdx  ];
1005                 vertices[outIdx+1] = (GLfloat)offset[1] + scale * tetrahedron_v[vertIdx+1];
1006                 vertices[outIdx+2] = (GLfloat)offset[2] + scale * tetrahedron_v[vertIdx+2];
1007
1008                 normals [outIdx  ] = tetrahedron_n[normIdx  ];
1009                 normals [outIdx+1] = tetrahedron_n[normIdx+1];
1010                 normals [outIdx+2] = tetrahedron_n[normIdx+2];
1011             }
1012         }
1013     }
1014     else if ( numLevels > 0 )
1015     {
1016         double local_offset[3] ;    /* Use a local variable to avoid buildup of roundoff errors */
1017         unsigned int stride = ipow(4,--numLevels)*TETRAHEDRON_VERT_ELEM_PER_OBJ;
1018         scale /= 2.0 ;
1019         for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ )
1020         {
1021             int idx         = i*3;
1022             local_offset[0] = offset[0] + scale * tetrahedron_v[idx  ];
1023             local_offset[1] = offset[1] + scale * tetrahedron_v[idx+1];
1024             local_offset[2] = offset[2] + scale * tetrahedron_v[idx+2];
1025             fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride );
1026         }
1027     }
1028 }
1029
1030 /* -- Now the various non-polyhedra (shapes involving circles) -- */
1031 /*
1032  * Compute lookup table of cos and sin values forming a circle
1033  * (or half circle if halfCircle==TRUE)
1034  *
1035  * Notes:
1036  *    It is the responsibility of the caller to free these tables
1037  *    The size of the table is (n+1) to form a connected loop
1038  *    The last entry is exactly the same as the first
1039  *    The sign of n can be flipped to get the reverse loop
1040  */
1041 static void fghCircleTable(GLfloat **sint, GLfloat **cost, const int n, const GLboolean halfCircle)
1042 {
1043     int i;
1044     
1045     /* Table size, the sign of n flips the circle direction */
1046     const int size = abs(n);
1047
1048     /* Determine the angle between samples */
1049     const GLfloat angle = (halfCircle?1:2)*(GLfloat)M_PI/(GLfloat)( ( n == 0 ) ? 1 : n );
1050
1051     /* Allocate memory for n samples, plus duplicate of first entry at the end */
1052     *sint = malloc(sizeof(GLfloat) * (size+1));
1053     *cost = malloc(sizeof(GLfloat) * (size+1));
1054
1055     /* Bail out if memory allocation fails, fgError never returns */
1056     if (!(*sint) || !(*cost))
1057     {
1058         free(*sint);
1059         free(*cost);
1060         fgError("Failed to allocate memory in fghCircleTable");
1061     }
1062
1063     /* Compute cos and sin around the circle */
1064     (*sint)[0] = 0.0;
1065     (*cost)[0] = 1.0;
1066
1067     for (i=1; i<size; i++)
1068     {
1069         (*sint)[i] = (GLfloat)sin(angle*i);
1070         (*cost)[i] = (GLfloat)cos(angle*i);
1071     }
1072
1073     
1074     if (halfCircle)
1075     {
1076         (*sint)[size] =  0.0f;  /* sin PI */
1077         (*cost)[size] = -1.0f;  /* cos PI */
1078     }
1079     else
1080     {
1081         /* Last sample is duplicate of the first (sin or cos of 2 PI) */
1082         (*sint)[size] = (*sint)[0];
1083         (*cost)[size] = (*cost)[0];
1084     }
1085 }
1086
1087 static void fghGenerateSphere(GLfloat radius, GLint slices, GLint stacks, GLfloat **vertices, GLfloat **normals, int* nVert)
1088 {
1089     int i,j;
1090     int idx = 0;    /* idx into vertex/normal buffer */
1091     GLfloat x,y,z;
1092
1093     /* Pre-computed circle */
1094     GLfloat *sint1,*cost1;
1095     GLfloat *sint2,*cost2;
1096
1097     /* number of unique vertices */
1098     if (slices==0 || stacks<2)
1099     {
1100         /* nothing to generate */
1101         *nVert = 0;
1102         return;
1103     }
1104     *nVert = slices*(stacks-1)+2;
1105     if ((*nVert) > 65535)
1106         /*
1107          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1108          * But still:
1109          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1110          */
1111         fgWarning("fghGenerateSphere: too many slices or stacks requested, indices will wrap");
1112
1113     /* precompute values on unit circle */
1114     fghCircleTable(&sint1,&cost1,-slices,FALSE);
1115     fghCircleTable(&sint2,&cost2, stacks,TRUE);
1116
1117     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1118     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1119     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1120     if (!(*vertices) || !(*normals))
1121     {
1122         free(*vertices);
1123         free(*normals);
1124         fgError("Failed to allocate memory in fghGenerateSphere");
1125     }
1126
1127     /* top */
1128     (*vertices)[0] = 0.f;
1129     (*vertices)[1] = 0.f;
1130     (*vertices)[2] = radius;
1131     (*normals )[0] = 0.f;
1132     (*normals )[1] = 0.f;
1133     (*normals )[2] = 1.f;
1134     idx = 3;
1135
1136     /* each stack */
1137     for( i=1; i<stacks; i++ )
1138     {
1139         for(j=0; j<slices; j++, idx+=3)
1140         {
1141             x = cost1[j]*sint2[i];
1142             y = sint1[j]*sint2[i];
1143             z = cost2[i];
1144
1145             (*vertices)[idx  ] = x*radius;
1146             (*vertices)[idx+1] = y*radius;
1147             (*vertices)[idx+2] = z*radius;
1148             (*normals )[idx  ] = x;
1149             (*normals )[idx+1] = y;
1150             (*normals )[idx+2] = z;
1151         }
1152     }
1153
1154     /* bottom */
1155     (*vertices)[idx  ] =  0.f;
1156     (*vertices)[idx+1] =  0.f;
1157     (*vertices)[idx+2] = -radius;
1158     (*normals )[idx  ] =  0.f;
1159     (*normals )[idx+1] =  0.f;
1160     (*normals )[idx+2] = -1.f;
1161
1162     /* Done creating vertices, release sin and cos tables */
1163     free(sint1);
1164     free(cost1);
1165     free(sint2);
1166     free(cost2);
1167 }
1168
1169 void fghGenerateCone(
1170     GLfloat base, GLfloat height, GLint slices, GLint stacks,   /*  input */
1171     GLfloat **vertices, GLfloat **normals, int* nVert           /* output */
1172     )
1173 {
1174     int i,j;
1175     int idx = 0;    /* idx into vertex/normal buffer */
1176
1177     /* Pre-computed circle */
1178     GLfloat *sint,*cost;
1179
1180     /* Step in z and radius as stacks are drawn. */
1181     GLfloat z = 0;
1182     GLfloat r = (GLfloat)base;
1183
1184     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1185     const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
1186
1187     /* Scaling factors for vertex normals */
1188     const GLfloat cosn = (GLfloat) (height / sqrt( height * height + base * base ));
1189     const GLfloat sinn = (GLfloat) (base   / sqrt( height * height + base * base ));
1190
1191
1192
1193     /* number of unique vertices */
1194     if (slices==0 || stacks<1)
1195     {
1196         /* nothing to generate */
1197         *nVert = 0;
1198         return;
1199     }
1200     *nVert = slices*(stacks+2)+1;   /* need an extra stack for closing off bottom with correct normals */
1201
1202     if ((*nVert) > 65535)
1203         /*
1204          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1205          * But still:
1206          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1207          */
1208         fgWarning("fghGenerateCone: too many slices or stacks requested, indices will wrap");
1209
1210     /* Pre-computed circle */
1211     fghCircleTable(&sint,&cost,-slices,FALSE);
1212
1213     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1214     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1215     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1216     if (!(*vertices) || !(*normals))
1217     {
1218         free(*vertices);
1219         free(*normals);
1220         fgError("Failed to allocate memory in fghGenerateCone");
1221     }
1222
1223     /* bottom */
1224     (*vertices)[0] =  0.f;
1225     (*vertices)[1] =  0.f;
1226     (*vertices)[2] =  z;
1227     (*normals )[0] =  0.f;
1228     (*normals )[1] =  0.f;
1229     (*normals )[2] = -1.f;
1230     idx = 3;
1231     /* other on bottom (get normals right) */
1232     for (j=0; j<slices; j++, idx+=3)
1233     {
1234         (*vertices)[idx  ] = cost[j]*r;
1235         (*vertices)[idx+1] = sint[j]*r;
1236         (*vertices)[idx+2] = z;
1237         (*normals )[idx  ] =  0.f;
1238         (*normals )[idx+1] =  0.f;
1239         (*normals )[idx+2] = -1.f;
1240     }
1241
1242     /* each stack */
1243     for (i=0; i<stacks+1; i++ )
1244     {
1245         for (j=0; j<slices; j++, idx+=3)
1246         {
1247             (*vertices)[idx  ] = cost[j]*r;
1248             (*vertices)[idx+1] = sint[j]*r;
1249             (*vertices)[idx+2] = z;
1250             (*normals )[idx  ] = cost[j]*sinn;
1251             (*normals )[idx+1] = sint[j]*sinn;
1252             (*normals )[idx+2] = cosn;
1253         }
1254
1255         z += zStep;
1256         r -= rStep;
1257     }
1258
1259     /* Release sin and cos tables */
1260     free(sint);
1261     free(cost);
1262 }
1263
1264 void fghGenerateCylinder(
1265     GLfloat radius, GLfloat height, GLint slices, GLint stacks, /*  input */
1266     GLfloat **vertices, GLfloat **normals, int* nVert           /* output */
1267     )
1268 {
1269     int i,j;
1270     int idx = 0;    /* idx into vertex/normal buffer */
1271
1272     /* Step in z as stacks are drawn. */
1273     GLfloat radf = (GLfloat)radius;
1274     GLfloat z;
1275     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1276
1277     /* Pre-computed circle */
1278     GLfloat *sint,*cost;
1279
1280     /* number of unique vertices */
1281     if (slices==0 || stacks<1)
1282     {
1283         /* nothing to generate */
1284         *nVert = 0;
1285         return;
1286     }
1287     *nVert = slices*(stacks+3)+2;   /* need two extra stacks for closing off top and bottom with correct normals */
1288
1289     if ((*nVert) > 65535)
1290         /*
1291          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1292          * But still:
1293          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1294          */
1295         fgWarning("fghGenerateCylinder: too many slices or stacks requested, indices will wrap");
1296
1297     /* Pre-computed circle */
1298     fghCircleTable(&sint,&cost,-slices,FALSE);
1299
1300     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1301     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1302     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1303     if (!(*vertices) || !(*normals))
1304     {
1305         free(*vertices);
1306         free(*normals);
1307         fgError("Failed to allocate memory in fghGenerateCylinder");
1308     }
1309
1310     z=0;
1311     /* top on Z-axis */
1312     (*vertices)[0] =  0.f;
1313     (*vertices)[1] =  0.f;
1314     (*vertices)[2] =  0.f;
1315     (*normals )[0] =  0.f;
1316     (*normals )[1] =  0.f;
1317     (*normals )[2] = -1.f;
1318     idx = 3;
1319     /* other on top (get normals right) */
1320     for (j=0; j<slices; j++, idx+=3)
1321     {
1322         (*vertices)[idx  ] = cost[j]*radf;
1323         (*vertices)[idx+1] = sint[j]*radf;
1324         (*vertices)[idx+2] = z;
1325         (*normals )[idx  ] = 0.f;
1326         (*normals )[idx+1] = 0.f;
1327         (*normals )[idx+2] = -1.f;
1328     }
1329
1330     /* each stack */
1331     for (i=0; i<stacks+1; i++ )
1332     {
1333         for (j=0; j<slices; j++, idx+=3)
1334         {
1335             (*vertices)[idx  ] = cost[j]*radf;
1336             (*vertices)[idx+1] = sint[j]*radf;
1337             (*vertices)[idx+2] = z;
1338             (*normals )[idx  ] = cost[j];
1339             (*normals )[idx+1] = sint[j];
1340             (*normals )[idx+2] = 0.f;
1341         }
1342
1343         z += zStep;
1344     }
1345
1346     /* other on bottom (get normals right) */
1347     z -= zStep;
1348     for (j=0; j<slices; j++, idx+=3)
1349     {
1350         (*vertices)[idx  ] = cost[j]*radf;
1351         (*vertices)[idx+1] = sint[j]*radf;
1352         (*vertices)[idx+2] = z;
1353         (*normals )[idx  ] = 0.f;
1354         (*normals )[idx+1] = 0.f;
1355         (*normals )[idx+2] = 1.f;
1356     }
1357
1358     /* bottom */
1359     (*vertices)[idx  ] =  0.f;
1360     (*vertices)[idx+1] =  0.f;
1361     (*vertices)[idx+2] =  height;
1362     (*normals )[idx  ] =  0.f;
1363     (*normals )[idx+1] =  0.f;
1364     (*normals )[idx+2] =  1.f;
1365
1366     /* Release sin and cos tables */
1367     free(sint);
1368     free(cost);
1369 }
1370
1371 void fghGenerateTorus(
1372     double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings, /*  input */
1373     GLfloat **vertices, GLfloat **normals, int* nVert                     /* output */
1374     )
1375 {
1376     GLfloat  iradius = (float)dInnerRadius;
1377     GLfloat  oradius = (float)dOuterRadius;
1378     int    i, j;
1379
1380     /* Pre-computed circle */
1381     GLfloat *spsi, *cpsi;
1382     GLfloat *sphi, *cphi;
1383
1384     /* number of unique vertices */
1385     if (nSides<2 || nRings<2)
1386     {
1387         /* nothing to generate */
1388         *nVert = 0;
1389         return;
1390     }
1391     *nVert = nSides * nRings;
1392
1393     if ((*nVert) > 65535)
1394         /*
1395          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1396          * But still:
1397          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1398          */
1399         fgWarning("fghGenerateTorus: too many slices or stacks requested, indices will wrap");
1400
1401     /* precompute values on unit circle */
1402     fghCircleTable(&spsi,&cpsi, nRings,FALSE);
1403     fghCircleTable(&sphi,&cphi,-nSides,FALSE);
1404
1405     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1406     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1407     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1408     if (!(*vertices) || !(*normals))
1409     {
1410         free(*vertices);
1411         free(*normals);
1412         fgError("Failed to allocate memory in fghGenerateTorus");
1413     }
1414
1415     for( j=0; j<nRings; j++ )
1416     {
1417         for( i=0; i<nSides; i++ )
1418         {
1419             int offset = 3 * ( j * nSides + i ) ;
1420
1421             (*vertices)[offset  ] = cpsi[j] * ( oradius + cphi[i] * iradius ) ;
1422             (*vertices)[offset+1] = spsi[j] * ( oradius + cphi[i] * iradius ) ;
1423             (*vertices)[offset+2] =                       sphi[i] * iradius  ;
1424             (*normals )[offset  ] = cpsi[j] * cphi[i] ;
1425             (*normals )[offset+1] = spsi[j] * cphi[i] ;
1426             (*normals )[offset+2] =           sphi[i] ;
1427         }
1428     }
1429
1430     /* Release sin and cos tables */
1431     free(spsi);
1432     free(cpsi);
1433     free(sphi);
1434     free(cphi);
1435 }
1436
1437 /* -- INTERNAL DRAWING functions --------------------------------------- */
1438 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
1439     static void fgh##nameICaps( GLboolean useWireMode )\
1440     {\
1441         if (!name##Cached)\
1442         {\
1443             fgh##nameICaps##Generate();\
1444             name##Cached = GL_TRUE;\
1445         }\
1446         \
1447         if (useWireMode)\
1448         {\
1449             fghDrawGeometryWire (name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ, \
1450                                  NULL,nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE,GL_LINE_LOOP,\
1451                                  NULL,0,0);\
1452         }\
1453         else\
1454         {\
1455             fghDrawGeometrySolid(name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ,\
1456                                  vertIdxs, 1, nameCaps##_VERT_PER_OBJ_TRI); \
1457         }\
1458     }
1459 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps)                        _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
1460 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
1461
1462 static void fghCube( GLfloat dSize, GLboolean useWireMode )
1463 {
1464     GLfloat *vertices;
1465
1466     if (!cubeCached)
1467     {
1468         fghCubeGenerate();
1469         cubeCached = GL_TRUE;
1470     }
1471
1472     if (dSize!=1.f)
1473     {
1474         /* Need to build new vertex list containing vertices for cube of different size */
1475         int i;
1476
1477         vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
1478
1479         /* Bail out if memory allocation fails, fgError never returns */
1480         if (!vertices)
1481         {
1482             free(vertices);
1483             fgError("Failed to allocate memory in fghCube");
1484         }
1485
1486         for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
1487             vertices[i] = dSize*cube_verts[i];
1488     }
1489     else
1490         vertices = cube_verts;
1491
1492     if (useWireMode)
1493         fghDrawGeometryWire(vertices, cube_norms, CUBE_VERT_PER_OBJ,
1494                             NULL,CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1495                             NULL,0,0);
1496     else
1497         fghDrawGeometrySolid(vertices, cube_norms, CUBE_VERT_PER_OBJ,
1498                              cube_vertIdxs, 1, CUBE_VERT_PER_OBJ_TRI);
1499
1500     if (dSize!=1.f)
1501         /* cleanup allocated memory */
1502         free(vertices);
1503 }
1504
1505 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
1506 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON)
1507 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON)
1508 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
1509 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON)
1510
1511 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
1512 {
1513     GLfloat *vertices;
1514     GLfloat * normals;
1515     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
1516     GLsizei    numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
1517     GLsizei    numFace = numTetr*TETRAHEDRON_NUM_FACES;
1518
1519     if (numTetr)
1520     {
1521         /* Allocate memory */
1522         vertices = malloc(numVert*3 * sizeof(GLfloat));
1523         normals  = malloc(numVert*3 * sizeof(GLfloat));
1524         /* Bail out if memory allocation fails, fgError never returns */
1525         if (!vertices || !normals)
1526         {
1527             free(vertices);
1528             free(normals);
1529             fgError("Failed to allocate memory in fghSierpinskiSponge");
1530         }
1531
1532         /* Generate elements */
1533         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
1534
1535         /* Draw and cleanup */
1536         if (useWireMode)
1537             fghDrawGeometryWire (vertices,normals,numVert,
1538                                  NULL,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1539                                  NULL,0,0);
1540         else
1541             fghDrawGeometrySolid(vertices,normals,numVert,NULL,1,0);
1542
1543         free(vertices);
1544         free(normals );
1545     }
1546 }
1547
1548
1549 static void fghSphere( GLfloat radius, GLint slices, GLint stacks, GLboolean useWireMode )
1550 {
1551     int i,j,idx, nVert;
1552     GLfloat *vertices, *normals;
1553
1554     /* Generate vertices and normals */
1555     fghGenerateSphere(radius,slices,stacks,&vertices,&normals,&nVert);
1556     
1557     if (nVert==0)
1558         /* nothing to draw */
1559         return;
1560
1561     if (useWireMode)
1562     {
1563         GLushort  *sliceIdx, *stackIdx;
1564         /* First, generate vertex index arrays for drawing with glDrawElements
1565          * We have a bunch of line_loops to draw for each stack, and a
1566          * bunch for each slice.
1567          */
1568
1569         sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1570         stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort));
1571         if (!(stackIdx) || !(sliceIdx))
1572         {
1573             free(stackIdx);
1574             free(sliceIdx);
1575             fgError("Failed to allocate memory in fghSphere");
1576         }
1577
1578         /* generate for each stack */
1579         for (i=0,idx=0; i<stacks-1; i++)
1580         {
1581             GLushort offset = 1+i*slices;           /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1582             for (j=0; j<slices; j++, idx++)
1583             {
1584                 stackIdx[idx] = offset+j;
1585             }
1586         }
1587
1588         /* generate for each slice */
1589         for (i=0,idx=0; i<slices; i++)
1590         {
1591             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1592             sliceIdx[idx++] = 0;                    /* vertex on top */
1593             for (j=0; j<stacks-1; j++, idx++)
1594             {
1595                 sliceIdx[idx] = offset+j*slices;
1596             }
1597             sliceIdx[idx++] = nVert-1;              /* zero based index, last element in array... */
1598         }
1599
1600         /* draw */
1601         fghDrawGeometryWire(vertices,normals,nVert,
1602             sliceIdx,slices,stacks+1,GL_LINE_STRIP,
1603             stackIdx,stacks-1,slices);
1604         
1605         /* cleanup allocated memory */
1606         free(sliceIdx);
1607         free(stackIdx);
1608     }
1609     else
1610     {
1611         /* First, generate vertex index arrays for drawing with glDrawElements
1612          * All stacks, including top and bottom are covered with a triangle
1613          * strip.
1614          */
1615         GLushort  *stripIdx;
1616         /* Create index vector */
1617         GLushort offset;
1618
1619         /* Allocate buffers for indices, bail out if memory allocation fails */
1620         stripIdx = malloc((slices+1)*2*(stacks)*sizeof(GLushort));
1621         if (!(stripIdx))
1622         {
1623             free(stripIdx);
1624             fgError("Failed to allocate memory in fghSphere");
1625         }
1626
1627         /* top stack */
1628         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1629         {
1630             stripIdx[idx  ] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1631             stripIdx[idx+1] = 0;
1632         }
1633         stripIdx[idx  ] = 1;                    /* repeat first slice's idx for closing off shape */
1634         stripIdx[idx+1] = 0;
1635         idx+=2;
1636
1637         /* middle stacks: */
1638         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1639         for (i=0; i<stacks-2; i++, idx+=2)
1640         {
1641             offset = 1+i*slices;                    /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1642             for (j=0; j<slices; j++, idx+=2)
1643             {
1644                 stripIdx[idx  ] = offset+j+slices;
1645                 stripIdx[idx+1] = offset+j;
1646             }
1647             stripIdx[idx  ] = offset+slices;        /* repeat first slice's idx for closing off shape */
1648             stripIdx[idx+1] = offset;
1649         }
1650
1651         /* bottom stack */
1652         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 */
1653         for (j=0; j<slices; j++, idx+=2)
1654         {
1655             stripIdx[idx  ] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1656             stripIdx[idx+1] = offset+j;
1657         }
1658         stripIdx[idx  ] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1659         stripIdx[idx+1] = offset;
1660
1661
1662         /* draw */
1663         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks,(slices+1)*2);
1664
1665         /* cleanup allocated memory */
1666         free(stripIdx);
1667     }
1668     
1669     /* cleanup allocated memory */
1670     free(vertices);
1671     free(normals);
1672 }
1673
1674 static void fghCone( GLfloat base, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1675 {
1676     int i,j,idx, nVert;
1677     GLfloat *vertices, *normals;
1678
1679     /* Generate vertices and normals */
1680     /* Note, (stacks+1)*slices vertices for side of object, slices+1 for top and bottom closures */
1681     fghGenerateCone(base,height,slices,stacks,&vertices,&normals,&nVert);
1682
1683     if (nVert==0)
1684         /* nothing to draw */
1685         return;
1686
1687     if (useWireMode)
1688     {
1689         GLushort  *sliceIdx, *stackIdx;
1690         /* First, generate vertex index arrays for drawing with glDrawElements
1691          * We have a bunch of line_loops to draw for each stack, and a
1692          * bunch for each slice.
1693          */
1694
1695         stackIdx = malloc(slices*stacks*sizeof(GLushort));
1696         sliceIdx = malloc(slices*2     *sizeof(GLushort));
1697         if (!(stackIdx) || !(sliceIdx))
1698         {
1699             free(stackIdx);
1700             free(sliceIdx);
1701             fgError("Failed to allocate memory in fghCone");
1702         }
1703
1704         /* generate for each stack */
1705         for (i=0,idx=0; i<stacks; i++)
1706         {
1707             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1708             for (j=0; j<slices; j++, idx++)
1709             {
1710                 stackIdx[idx] = offset+j;
1711             }
1712         }
1713
1714         /* generate for each slice */
1715         for (i=0,idx=0; i<slices; i++)
1716         {
1717             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1718             sliceIdx[idx++] = offset+slices;
1719             sliceIdx[idx++] = offset+(stacks+1)*slices;
1720         }
1721
1722         /* draw */
1723         fghDrawGeometryWire(vertices,normals,nVert,
1724             sliceIdx,1,slices*2,GL_LINES,
1725             stackIdx,stacks,slices);
1726
1727         /* cleanup allocated memory */
1728         free(sliceIdx);
1729         free(stackIdx);
1730     }
1731     else
1732     {
1733         /* First, generate vertex index arrays for drawing with glDrawElements
1734          * All stacks, including top and bottom are covered with a triangle
1735          * strip.
1736          */
1737         GLushort  *stripIdx;
1738         /* Create index vector */
1739         GLushort offset;
1740
1741         /* Allocate buffers for indices, bail out if memory allocation fails */
1742         stripIdx = malloc((slices+1)*2*(stacks+1)*sizeof(GLushort));    /*stacks +1 because of closing off bottom */
1743         if (!(stripIdx))
1744         {
1745             free(stripIdx);
1746             fgError("Failed to allocate memory in fghCone");
1747         }
1748
1749         /* top stack */
1750         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1751         {
1752             stripIdx[idx  ] = 0;
1753             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1754         }
1755         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1756         stripIdx[idx+1] = 1;
1757         idx+=2;
1758
1759         /* middle stacks: */
1760         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1761         for (i=0; i<stacks; i++, idx+=2)
1762         {
1763             offset = 1+(i+1)*slices;                /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1764             for (j=0; j<slices; j++, idx+=2)
1765             {
1766                 stripIdx[idx  ] = offset+j;
1767                 stripIdx[idx+1] = offset+j+slices;
1768             }
1769             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1770             stripIdx[idx+1] = offset+slices;
1771         }
1772
1773         /* draw */
1774         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks+1,(slices+1)*2);
1775
1776         /* cleanup allocated memory */
1777         free(stripIdx);
1778     }
1779
1780     /* cleanup allocated memory */
1781     free(vertices);
1782     free(normals);
1783 }
1784
1785 static void fghCylinder( GLfloat radius, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1786 {
1787     int i,j,idx, nVert;
1788     GLfloat *vertices, *normals;
1789
1790     /* Generate vertices and normals */
1791     /* Note, (stacks+1)*slices vertices for side of object, 2*slices+2 for top and bottom closures */
1792     fghGenerateCylinder(radius,height,slices,stacks,&vertices,&normals,&nVert);
1793
1794     if (nVert==0)
1795         /* nothing to draw */
1796         return;
1797
1798     if (useWireMode)
1799     {
1800         GLushort  *sliceIdx, *stackIdx;
1801         /* First, generate vertex index arrays for drawing with glDrawElements
1802          * We have a bunch of line_loops to draw for each stack, and a
1803          * bunch for each slice.
1804          */
1805
1806         stackIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1807         sliceIdx = malloc(slices*2         *sizeof(GLushort));
1808         if (!(stackIdx) || !(sliceIdx))
1809         {
1810             free(stackIdx);
1811             free(sliceIdx);
1812             fgError("Failed to allocate memory in fghCylinder");
1813         }
1814
1815         /* generate for each stack */
1816         for (i=0,idx=0; i<stacks+1; i++)
1817         {
1818             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1819             for (j=0; j<slices; j++, idx++)
1820             {
1821                 stackIdx[idx] = offset+j;
1822             }
1823         }
1824
1825         /* generate for each slice */
1826         for (i=0,idx=0; i<slices; i++)
1827         {
1828             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1829             sliceIdx[idx++] = offset+slices;
1830             sliceIdx[idx++] = offset+(stacks+1)*slices;
1831         }
1832
1833         /* draw */
1834         fghDrawGeometryWire(vertices,normals,nVert,
1835             sliceIdx,1,slices*2,GL_LINES,
1836             stackIdx,stacks+1,slices);
1837
1838         /* cleanup allocated memory */
1839         free(sliceIdx);
1840         free(stackIdx);
1841     }
1842     else
1843     {
1844         /* First, generate vertex index arrays for drawing with glDrawElements
1845          * All stacks, including top and bottom are covered with a triangle
1846          * strip.
1847          */
1848         GLushort  *stripIdx;
1849         /* Create index vector */
1850         GLushort offset;
1851
1852         /* Allocate buffers for indices, bail out if memory allocation fails */
1853         stripIdx = malloc((slices+1)*2*(stacks+2)*sizeof(GLushort));    /*stacks +2 because of closing off bottom and top */
1854         if (!(stripIdx))
1855         {
1856             free(stripIdx);
1857             fgError("Failed to allocate memory in fghCylinder");
1858         }
1859
1860         /* top stack */
1861         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1862         {
1863             stripIdx[idx  ] = 0;
1864             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1865         }
1866         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1867         stripIdx[idx+1] = 1;
1868         idx+=2;
1869
1870         /* middle stacks: */
1871         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1872         for (i=0; i<stacks; i++, idx+=2)
1873         {
1874             offset = 1+(i+1)*slices;                /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1875             for (j=0; j<slices; j++, idx+=2)
1876             {
1877                 stripIdx[idx  ] = offset+j;
1878                 stripIdx[idx+1] = offset+j+slices;
1879             }
1880             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1881             stripIdx[idx+1] = offset+slices;
1882         }
1883
1884         /* top stack */
1885         offset = 1+(stacks+2)*slices;
1886         for (j=0; j<slices; j++, idx+=2)
1887         {
1888             stripIdx[idx  ] = offset+j;
1889             stripIdx[idx+1] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1890         }
1891         stripIdx[idx  ] = offset;
1892         stripIdx[idx+1] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1893
1894         /* draw */
1895         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks+2,(slices+1)*2);
1896
1897         /* cleanup allocated memory */
1898         free(stripIdx);
1899     }
1900
1901     /* cleanup allocated memory */
1902     free(vertices);
1903     free(normals);
1904 }
1905
1906 static void fghTorus( GLfloat dInnerRadius, GLfloat dOuterRadius, GLint nSides, GLint nRings, GLboolean useWireMode )
1907 {
1908     int i,j,idx, nVert;
1909     GLfloat *vertices, *normals;
1910
1911     /* Generate vertices and normals */
1912     fghGenerateTorus(dInnerRadius,dOuterRadius,nSides,nRings, &vertices,&normals,&nVert);
1913
1914     if (nVert==0)
1915         /* nothing to draw */
1916         return;
1917
1918     if (useWireMode)
1919     {
1920         GLushort  *sideIdx, *ringIdx;
1921         /* First, generate vertex index arrays for drawing with glDrawElements
1922          * We have a bunch of line_loops to draw each side, and a
1923          * bunch for each ring.
1924          */
1925
1926         ringIdx = malloc(nRings*nSides*sizeof(GLushort));
1927         sideIdx = malloc(nSides*nRings*sizeof(GLushort));
1928         if (!(ringIdx) || !(sideIdx))
1929         {
1930             free(ringIdx);
1931             free(sideIdx);
1932             fgError("Failed to allocate memory in fghTorus");
1933         }
1934
1935         /* generate for each ring */
1936         for( j=0,idx=0; j<nRings; j++ )
1937             for( i=0; i<nSides; i++, idx++ )
1938                 ringIdx[idx] = j * nSides + i;
1939
1940         /* generate for each side */
1941         for( i=0,idx=0; i<nSides; i++ )
1942             for( j=0; j<nRings; j++, idx++ )
1943                 sideIdx[idx] = j * nSides + i;
1944
1945         /* draw */
1946         fghDrawGeometryWire(vertices,normals,nVert,
1947             ringIdx,nRings,nSides,GL_LINE_LOOP,
1948             sideIdx,nSides,nRings);
1949         
1950         /* cleanup allocated memory */
1951         free(sideIdx);
1952         free(ringIdx);
1953     }
1954     else
1955     {
1956         /* First, generate vertex index arrays for drawing with glDrawElements
1957          * All stacks, including top and bottom are covered with a triangle
1958          * strip.
1959          */
1960         GLushort  *stripIdx;
1961
1962         /* Allocate buffers for indices, bail out if memory allocation fails */
1963         stripIdx = malloc((nRings+1)*2*nSides*sizeof(GLushort));
1964         if (!(stripIdx))
1965         {
1966             free(stripIdx);
1967             fgError("Failed to allocate memory in fghTorus");
1968         }
1969
1970         for( i=0, idx=0; i<nSides; i++ )
1971         {
1972             int ioff = 1;
1973             if (i==nSides-1)
1974                 ioff = -i;
1975
1976             for( j=0; j<nRings; j++, idx+=2 )
1977             {
1978                 int offset = j * nSides + i;
1979                 stripIdx[idx  ] = offset;
1980                 stripIdx[idx+1] = offset + ioff;
1981             }
1982             /* repeat first to close off shape */
1983             stripIdx[idx  ] = i;
1984             stripIdx[idx+1] = i + ioff;
1985             idx +=2;
1986         }
1987
1988         /* draw */
1989         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,nSides,(nRings+1)*2);
1990
1991         /* cleanup allocated memory */
1992         free(stripIdx);
1993     }
1994
1995     /* cleanup allocated memory */
1996     free(vertices);
1997     free(normals);
1998 }
1999
2000
2001 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
2002
2003
2004 /*
2005  * Draws a solid sphere
2006  */
2007 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
2008 {
2009     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
2010     fghSphere((GLfloat)radius, slices, stacks, FALSE );
2011 }
2012
2013 /*
2014  * Draws a wire sphere
2015  */
2016 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
2017 {
2018     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
2019     fghSphere((GLfloat)radius, slices, stacks, TRUE );
2020     
2021 }
2022
2023 /*
2024  * Draws a solid cone
2025  */
2026 void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks )
2027 {
2028     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
2029     fghCone((GLfloat)base, (GLfloat)height, slices, stacks, FALSE );
2030 }
2031
2032 /*
2033  * Draws a wire cone
2034  */
2035 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
2036 {
2037     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
2038     fghCone((GLfloat)base, (GLfloat)height, slices, stacks, TRUE );
2039 }
2040
2041
2042 /*
2043  * Draws a solid cylinder
2044  */
2045 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
2046 {
2047     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
2048     fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, FALSE );
2049 }
2050
2051 /*
2052  * Draws a wire cylinder
2053  */
2054 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
2055 {
2056     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
2057     fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, TRUE );
2058 }
2059
2060 /*
2061  * Draws a wire torus
2062  */
2063 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
2064 {
2065     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
2066     fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, TRUE);
2067 }
2068
2069 /*
2070  * Draws a solid torus
2071  */
2072 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
2073 {
2074     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
2075     fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, FALSE);
2076 }
2077
2078
2079
2080 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
2081 /* Macro to generate interface functions */
2082 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
2083     void FGAPIENTRY glutWire##nameICaps( void )\
2084     {\
2085         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
2086         fgh##nameICaps( TRUE );\
2087     }\
2088     void FGAPIENTRY glutSolid##nameICaps( void )\
2089     {\
2090         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
2091         fgh##nameICaps( FALSE );\
2092     }
2093
2094 void FGAPIENTRY glutWireCube( double dSize )
2095 {
2096     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
2097     fghCube( (GLfloat)dSize, TRUE );
2098 }
2099 void FGAPIENTRY glutSolidCube( double dSize )
2100 {
2101     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
2102     fghCube( (GLfloat)dSize, FALSE );
2103 }
2104
2105 DECLARE_SHAPE_INTERFACE(Dodecahedron)
2106 DECLARE_SHAPE_INTERFACE(Icosahedron)
2107 DECLARE_SHAPE_INTERFACE(Octahedron)
2108 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron)
2109
2110 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
2111 {
2112     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
2113     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, TRUE );
2114 }
2115 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
2116 {
2117     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
2118     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, FALSE );
2119 }
2120
2121 DECLARE_SHAPE_INTERFACE(Tetrahedron)
2122
2123
2124 /*** END OF FILE ***/