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