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