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