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