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