e41f01dbfdf0e6ccc52d4b4480e49ca002967d9b
[freeglut] / src / fg_geometry.c
1 /*
2  * fg_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 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
48                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
49                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
50     );
51 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
52                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
53 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
54                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
55                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
56                                   GLint attribute_v_coord, GLint attribute_v_normal
57     );
58 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
59                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
60                                    GLint attribute_v_coord, GLint attribute_v_normal, GLint attribute_v_texture);
61 /* declare function for generating visualization of normals */
62 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices);
63 static void fghDrawNormalVisualization11();
64 static void fghDrawNormalVisualization20(GLint attribute_v_coord);
65
66 /* Drawing geometry:
67  * Explanation of the functions has to be separate for the polyhedra and
68  * the non-polyhedra (objects with a circular cross-section).
69  * Polyhedra:
70  *   - We have only implemented the five platonic solids and the rhomboid
71  *     dodecahedron. If you need more types of polyhedra, please see
72  *     CPolyhedron in MRPT
73  *   - Solids are drawn by glDrawArrays if composed of triangular faces
74  *     (the tetrahedron, octahedron, and icosahedron), or are first
75  *     decomposed into triangles and then drawn by glDrawElements if its
76  *     faces are squares or pentagons (cube, dodecahedron and rhombic
77  *     dodecahedron) as some vertices are repeated in that case.
78  *   - WireFrame drawing is done using a GL_LINE_LOOP per face, and thus
79  *     issuing one draw call per face. glDrawArrays is always used as no
80  *     triangle decomposition is needed to draw faces. We use the "first"
81  *     parameter in glDrawArrays to go from face to face.
82  * 
83  * Non-polyhedra:
84  *   - We have implemented the sphere, cylinder, cone and torus.
85  *   - All shapes are characterized by two parameters: the number of
86  *     subdivisions along two axes used to construct the shape's vertices 
87  *     (e.g. stacks and slices for the sphere).
88  *     As different subdivisions are most suitable for different shapes,
89  *     and are thus also named differently, I wont provide general comments
90  *     on them here.
91  *   - Solids are drawn using glDrawArrays and GL_TRIANGLE_STRIP. Each
92  *     strip covers one revolution around one of the two subdivision axes
93  *     of the shape.
94  *   - WireFrame drawing is done for the subdivisions along the two axes 
95  *     separately, usually using GL_LINE_LOOP. Vertex index arrays are
96  *     built containing the vertices to be drawn for each loop, which are
97  *     then drawn using multiple calls to glDrawElements. As the number of
98  *     subdivisions along the two axes is not guaranteed to be equal, the
99  *     vertex indices for e.g. stacks and slices are stored in separate
100  *     arrays, which makes the input to the drawing function a bit clunky,
101  *     but allows for the same drawing function to be used for all shapes.
102  */
103
104
105 /**
106  * Draw geometric shape in wire mode (only edges)
107  *
108  * Arguments:
109  * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
110  *   The vertex coordinate and normal buffers, and the number of entries in
111  *   those
112  * GLushort *vertIdxs
113  *   a vertex indices buffer, optional (never passed for the polyhedra)
114  * GLsizei numParts, GLsizei numVertPerPart
115  *   polyhedra: number of faces, and the number of vertices for drawing
116  *     each face
117  *   non-polyhedra: number of edges to draw for first subdivision (not
118  *     necessarily equal to number of subdivisions requested by user, e.g.
119  *     as each subdivision is enclosed by two edges), and number of
120  *     vertices for drawing each
121  *   numParts * numVertPerPart gives the number of entries in the vertex
122  *     array vertIdxs
123  * GLenum vertexMode
124  *   vertex drawing mode (e.g. always GL_LINE_LOOP for polyhedra, varies
125  *   for others)
126  * GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
127  *   non-polyhedra only: same as the above, but now for subdivisions along
128  *   the other axis. Always drawn as GL_LINE_LOOP.
129  *
130  * Feel free to contribute better naming ;)
131  */
132 void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
133                                 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
134                                 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
135     )
136 {
137     GLint attribute_v_coord  = fgStructure.CurrentWindow->Window.attribute_v_coord;
138     GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal;
139
140     if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
141         /* User requested a 2.0 draw */
142         fghDrawGeometryWire20(vertices, normals, numVertices,
143                               vertIdxs, numParts, numVertPerPart, vertexMode,
144                               vertIdxs2, numParts2, numVertPerPart2,
145                               attribute_v_coord, attribute_v_normal);
146     else
147         fghDrawGeometryWire11(vertices, normals,
148                               vertIdxs, numParts, numVertPerPart, vertexMode,
149                               vertIdxs2, numParts2, numVertPerPart2);
150 }
151
152 /* Draw the geometric shape with filled triangles
153  *
154  * Arguments:
155  * GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices
156  *   The vertex coordinate, normal and texture coordinate buffers, and the
157  *   number of entries in those
158  * GLushort *vertIdxs
159  *   a vertex indices buffer, optional (not passed for the polyhedra with
160  *   triangular faces)
161  * GLsizei numParts, GLsizei numVertPerPart
162  *   polyhedra: not used for polyhedra with triangular faces
163        (numEdgePerFace==3), as each vertex+normal pair is drawn only once,
164        so no vertex indices are used.
165        Else, the shape was triangulated (DECOMPOSE_TO_TRIANGLE), leading to
166        reuse of some vertex+normal pairs, and thus the need to draw with
167        glDrawElements. numParts is always 1 in this case (we can draw the
168        whole object with one call to glDrawElements as the vertex index
169        array contains separate triangles), and numVertPerPart indicates
170        the number of vertex indices in the vertex array.
171  *   non-polyhedra: number of parts (GL_TRIANGLE_STRIPs) to be drawn
172        separately (numParts calls to glDrawElements) to create the object.
173        numVertPerPart indicates the number of vertex indices to be
174        processed at each draw call.
175  *   numParts * numVertPerPart gives the number of entries in the vertex
176  *     array vertIdxs
177  */
178 void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
179                           GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
180 {
181     GLint attribute_v_coord   = fgStructure.CurrentWindow->Window.attribute_v_coord;
182     GLint attribute_v_normal  = fgStructure.CurrentWindow->Window.attribute_v_normal;
183     GLint attribute_v_texture = fgStructure.CurrentWindow->Window.attribute_v_texture;
184
185     if (fgStructure.CurrentWindow->State.VisualizeNormals)
186         /* generate normals for each vertex to be drawn as well */
187         fghGenerateNormalVisualization(vertices, normals, numVertices);
188
189     if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
190     {
191         /* User requested a 2.0 draw */
192         fghDrawGeometrySolid20(vertices, normals, textcs, numVertices,
193                                vertIdxs, numParts, numVertIdxsPerPart,
194                                attribute_v_coord, attribute_v_normal, attribute_v_texture);
195
196         if (fgStructure.CurrentWindow->State.VisualizeNormals)
197             /* draw normals for each vertex as well */
198             fghDrawNormalVisualization20(attribute_v_coord);
199     }
200     else
201     {
202         fghDrawGeometrySolid11(vertices, normals, textcs, numVertices,
203                                vertIdxs, numParts, numVertIdxsPerPart);
204
205         if (fgStructure.CurrentWindow->State.VisualizeNormals)
206             /* draw normals for each vertex as well */
207             fghDrawNormalVisualization11();
208     }
209 }
210
211
212
213 /* Version for OpenGL (ES) 1.1 */
214 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
215                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
216                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
217     )
218 {
219     int i;
220     
221     glEnableClientState(GL_VERTEX_ARRAY);
222     glEnableClientState(GL_NORMAL_ARRAY);
223
224     glVertexPointer(3, GL_FLOAT, 0, vertices);
225     glNormalPointer(GL_FLOAT, 0, normals);
226
227     
228     if (!vertIdxs)
229         /* Draw per face (TODO: could use glMultiDrawArrays if available) */
230         for (i=0; i<numParts; i++)
231             glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
232     else
233         for (i=0; i<numParts; i++)
234             glDrawElements(vertexMode,numVertPerPart,GL_UNSIGNED_SHORT,vertIdxs+i*numVertPerPart);
235
236     if (vertIdxs2)
237         for (i=0; i<numParts2; i++)
238             glDrawElements(GL_LINE_LOOP,numVertPerPart2,GL_UNSIGNED_SHORT,vertIdxs2+i*numVertPerPart2);
239
240     glDisableClientState(GL_VERTEX_ARRAY);
241     glDisableClientState(GL_NORMAL_ARRAY);
242 }
243
244
245 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
246                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
247 {
248     int i;
249
250     glEnableClientState(GL_VERTEX_ARRAY);
251     glEnableClientState(GL_NORMAL_ARRAY);
252
253     glVertexPointer(3, GL_FLOAT, 0, vertices);
254     glNormalPointer(GL_FLOAT, 0, normals);
255
256     if (textcs)
257     {
258         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
259         glTexCoordPointer(2, GL_FLOAT, 0, textcs);
260     }
261
262     if (!vertIdxs)
263         glDrawArrays(GL_TRIANGLES, 0, numVertices);
264     else
265         if (numParts>1)
266             for (i=0; i<numParts; i++)
267                 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs+i*numVertIdxsPerPart);
268         else
269             glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs);
270
271     glDisableClientState(GL_VERTEX_ARRAY);
272     glDisableClientState(GL_NORMAL_ARRAY);
273     if (textcs)
274         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
275 }
276
277 /* Version for OpenGL (ES) >= 2.0 */
278 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
279                                   GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
280                                   GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
281                                   GLint attribute_v_coord, GLint attribute_v_normal)
282 {
283     GLuint vbo_coords = 0, vbo_normals = 0,
284         ibo_elements = 0, ibo_elements2 = 0;
285     GLsizei numVertIdxs = numParts * numVertPerPart;
286     GLsizei numVertIdxs2 = numParts2 * numVertPerPart2;
287     int i;
288
289     if (numVertices > 0 && attribute_v_coord != -1) {
290         fghGenBuffers(1, &vbo_coords);
291         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
292         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
293                       vertices, FGH_STATIC_DRAW);
294     }
295     
296     if (numVertices > 0 && attribute_v_normal != -1) {
297         fghGenBuffers(1, &vbo_normals);
298         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
299         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
300                       normals, FGH_STATIC_DRAW);
301     }
302     
303     if (vertIdxs != NULL) {
304         fghGenBuffers(1, &ibo_elements);
305         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
306         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
307                       vertIdxs, FGH_STATIC_DRAW);
308         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
309     }
310
311     if (vertIdxs2 != NULL) {
312         fghGenBuffers(1, &ibo_elements2);
313         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
314         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs2 * sizeof(vertIdxs2[0]),
315                       vertIdxs2, FGH_STATIC_DRAW);
316         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
317     }
318
319     if (vbo_coords) {
320         fghEnableVertexAttribArray(attribute_v_coord);
321         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
322         fghVertexAttribPointer(
323             attribute_v_coord,  /* attribute */
324             3,                  /* number of elements per vertex, here (x,y,z) */
325             GL_FLOAT,           /* the type of each element */
326             GL_FALSE,           /* take our values as-is */
327             0,                  /* no extra data between each position */
328             0                   /* offset of first element */
329         );
330         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
331     }
332
333     if (vbo_normals) {
334         fghEnableVertexAttribArray(attribute_v_normal);
335         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
336         fghVertexAttribPointer(
337             attribute_v_normal, /* attribute */
338             3,                  /* number of elements per vertex, here (x,y,z) */
339             GL_FLOAT,           /* the type of each element */
340             GL_FALSE,           /* take our values as-is */
341             0,                  /* no extra data between each position */
342             0                   /* offset of first element */
343         );
344         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
345     }
346
347     if (!vertIdxs) {
348         /* Draw per face (TODO: could use glMultiDrawArrays if available) */
349         for (i=0; i<numParts; i++)
350             glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
351     } else {
352         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
353         for (i=0; i<numParts; i++)
354             glDrawElements(vertexMode, numVertPerPart,
355                            GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertPerPart));
356         /* Clean existing bindings before clean-up */
357         /* Android showed instability otherwise */
358         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
359     }
360
361     if (vertIdxs2) {
362         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
363         for (i=0; i<numParts2; i++)
364             glDrawElements(GL_LINE_LOOP, numVertPerPart2,
365                            GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs2[0])*i*numVertPerPart2));
366         /* Clean existing bindings before clean-up */
367         /* Android showed instability otherwise */
368         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
369     }
370     
371     if (vbo_coords != 0)
372         fghDisableVertexAttribArray(attribute_v_coord);
373     if (vbo_normals != 0)
374         fghDisableVertexAttribArray(attribute_v_normal);
375     
376     if (vbo_coords != 0)
377         fghDeleteBuffers(1, &vbo_coords);
378     if (vbo_normals != 0)
379         fghDeleteBuffers(1, &vbo_normals);
380     if (ibo_elements != 0)
381         fghDeleteBuffers(1, &ibo_elements);
382     if (ibo_elements2 != 0)
383         fghDeleteBuffers(1, &ibo_elements2);
384 }
385
386
387
388
389 /* Version for OpenGL (ES) >= 2.0 */
390 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
391                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
392                                    GLint attribute_v_coord, GLint attribute_v_normal, GLint attribute_v_texture)
393 {
394     GLuint vbo_coords = 0, vbo_normals = 0, vbo_textcs = 0, ibo_elements = 0;
395     GLsizei numVertIdxs = numParts * numVertIdxsPerPart;
396     int i;
397   
398     if (numVertices > 0 && attribute_v_coord != -1) {
399         fghGenBuffers(1, &vbo_coords);
400         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
401         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
402                       vertices, FGH_STATIC_DRAW);
403         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
404     }
405     
406     if (numVertices > 0 && attribute_v_normal != -1) {
407         fghGenBuffers(1, &vbo_normals);
408         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
409         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
410                       normals, FGH_STATIC_DRAW);
411         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
412     }
413
414     if (numVertices > 0 && attribute_v_texture != -1 && textcs) {
415         fghGenBuffers(1, &vbo_textcs);
416         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs);
417         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 2 * sizeof(textcs[0]),
418                       textcs, FGH_STATIC_DRAW);
419         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
420     }
421     
422     if (vertIdxs != NULL) {
423         fghGenBuffers(1, &ibo_elements);
424         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
425         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
426                       vertIdxs, FGH_STATIC_DRAW);
427         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
428     }
429     
430     if (vbo_coords) {
431         fghEnableVertexAttribArray(attribute_v_coord);
432         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
433         fghVertexAttribPointer(
434             attribute_v_coord,  /* attribute */
435             3,                  /* number of elements per vertex, here (x,y,z) */
436             GL_FLOAT,           /* the type of each element */
437             GL_FALSE,           /* take our values as-is */
438             0,                  /* no extra data between each position */
439             0                   /* offset of first element */
440         );
441         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
442     };
443     
444     if (vbo_normals) {
445         fghEnableVertexAttribArray(attribute_v_normal);
446         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
447         fghVertexAttribPointer(
448             attribute_v_normal, /* attribute */
449             3,                  /* number of elements per vertex, here (x,y,z) */
450             GL_FLOAT,           /* the type of each element */
451             GL_FALSE,           /* take our values as-is */
452             0,                  /* no extra data between each position */
453             0                   /* offset of first element */
454         );
455         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
456     };
457
458     if (vbo_textcs) {
459         fghEnableVertexAttribArray(attribute_v_texture);
460         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs);
461         fghVertexAttribPointer(
462             attribute_v_texture,/* attribute */
463             2,                  /* number of elements per vertex, here (s,t) */
464             GL_FLOAT,           /* the type of each element */
465             GL_FALSE,           /* take our values as-is */
466             0,                  /* no extra data between each position */
467             0                   /* offset of first element */
468             );
469         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
470     };
471     
472     if (vertIdxs == NULL) {
473         glDrawArrays(GL_TRIANGLES, 0, numVertices);
474     } else {
475         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
476         if (numParts>1) {
477             for (i=0; i<numParts; i++) {
478                 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertIdxsPerPart));
479             }
480         } else {
481             glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, 0);
482         }
483         /* Clean existing bindings before clean-up */
484         /* Android showed instability otherwise */
485         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
486     }
487     
488     if (vbo_coords != 0)
489         fghDisableVertexAttribArray(attribute_v_coord);
490     if (vbo_normals != 0)
491         fghDisableVertexAttribArray(attribute_v_normal);
492     if (vbo_textcs != 0)
493         fghDisableVertexAttribArray(attribute_v_texture);
494     
495     if (vbo_coords != 0)
496         fghDeleteBuffers(1, &vbo_coords);
497     if (vbo_normals != 0)
498         fghDeleteBuffers(1, &vbo_normals);
499     if (vbo_textcs != 0)
500         fghDeleteBuffers(1, &vbo_textcs);
501     if (ibo_elements != 0)
502         fghDeleteBuffers(1, &ibo_elements);
503 }
504
505
506
507 /**
508  * Generate vertex indices for visualizing the normals.
509  * vertices are written into verticesForNormalVisualization.
510  * This must be freed by caller, we do the free at the
511  * end of fghDrawNormalVisualization11/fghDrawNormalVisualization20
512  */
513 static GLfloat *verticesForNormalVisualization;
514 static GLsizei numNormalVertices = 0;
515 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices)
516 {
517     int i,j;
518     numNormalVertices = numVertices * 2;
519     verticesForNormalVisualization = malloc(numNormalVertices*3 * sizeof(GLfloat));
520
521     for (i=0,j=0; i<numNormalVertices*3/2; i+=3, j+=6)
522     {
523         verticesForNormalVisualization[j+0] = vertices[i+0];
524         verticesForNormalVisualization[j+1] = vertices[i+1];
525         verticesForNormalVisualization[j+2] = vertices[i+2];
526         verticesForNormalVisualization[j+3] = vertices[i+0] + normals[i+0]/4.f;
527         verticesForNormalVisualization[j+4] = vertices[i+1] + normals[i+1]/4.f;
528         verticesForNormalVisualization[j+5] = vertices[i+2] + normals[i+2]/4.f;
529     }
530 }
531
532 /* Version for OpenGL (ES) 1.1 */
533 static void fghDrawNormalVisualization11()
534 {
535     GLfloat currentColor[4];
536     /* Setup draw color: (1,1,1)-shape's color */
537     glGetFloatv(GL_CURRENT_COLOR,currentColor);
538     glColor4f(1-currentColor[0],1-currentColor[1],1-currentColor[2],currentColor[3]);
539
540     glEnableClientState(GL_VERTEX_ARRAY);
541
542     glVertexPointer(3, GL_FLOAT, 0, verticesForNormalVisualization);
543     glDrawArrays(GL_LINES, 0, numNormalVertices);
544
545     glDisableClientState(GL_VERTEX_ARRAY);
546
547     /* Done, free memory, reset color */
548     free(verticesForNormalVisualization);
549     glColor4f(currentColor[0],currentColor[1],currentColor[2],currentColor[3]);
550 }
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,NULL,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, NULL, 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,NULL,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,NULL,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,NULL,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,NULL,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,NULL,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 ***/