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