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