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