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