no longer using sinf/cosfsqrtf in fg_geometry, no need for all this
[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 }
899
900 static void fghSierpinskiSpongeGenerate ( int numLevels, double offset[3], GLfloat scale, GLfloat* vertices, GLfloat* normals )
901 {
902     int i, j;
903     if ( numLevels == 0 )
904     {
905         for (i=0; i<TETRAHEDRON_NUM_FACES; i++)
906         {
907             int normIdx         = i*3;
908             int faceIdxVertIdx  = i*TETRAHEDRON_NUM_EDGE_PER_FACE;
909             for (j=0; j<TETRAHEDRON_NUM_EDGE_PER_FACE; j++)
910             {
911                 int outIdx  = i*TETRAHEDRON_NUM_EDGE_PER_FACE*3+j*3;
912                 int vertIdx = tetrahedron_vi[faceIdxVertIdx+j]*3;
913
914                 vertices[outIdx  ] = (GLfloat)offset[0] + scale * tetrahedron_v[vertIdx  ];
915                 vertices[outIdx+1] = (GLfloat)offset[1] + scale * tetrahedron_v[vertIdx+1];
916                 vertices[outIdx+2] = (GLfloat)offset[2] + scale * tetrahedron_v[vertIdx+2];
917
918                 normals [outIdx  ] = tetrahedron_n[normIdx  ];
919                 normals [outIdx+1] = tetrahedron_n[normIdx+1];
920                 normals [outIdx+2] = tetrahedron_n[normIdx+2];
921             }
922         }
923     }
924     else if ( numLevels > 0 )
925     {
926         double local_offset[3] ;    /* Use a local variable to avoid buildup of roundoff errors */
927         unsigned int stride = ipow(4,--numLevels)*TETRAHEDRON_VERT_ELEM_PER_OBJ;
928         scale /= 2.0 ;
929         for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ )
930         {
931             int idx         = i*3;
932             local_offset[0] = offset[0] + scale * tetrahedron_v[idx  ];
933             local_offset[1] = offset[1] + scale * tetrahedron_v[idx+1];
934             local_offset[2] = offset[2] + scale * tetrahedron_v[idx+2];
935             fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride );
936         }
937     }
938 }
939
940 /* -- Now the various non-polyhedra (shapes involving circles) -- */
941 /*
942  * Compute lookup table of cos and sin values forming a circle
943  * (or half circle if halfCircle==TRUE)
944  *
945  * Notes:
946  *    It is the responsibility of the caller to free these tables
947  *    The size of the table is (n+1) to form a connected loop
948  *    The last entry is exactly the same as the first
949  *    The sign of n can be flipped to get the reverse loop
950  */
951 static void fghCircleTable(GLfloat **sint, GLfloat **cost, const int n, const GLboolean halfCircle)
952 {
953     int i;
954     
955     /* Table size, the sign of n flips the circle direction */
956     const int size = abs(n);
957
958     /* Determine the angle between samples */
959     const GLfloat angle = (halfCircle?1:2)*(GLfloat)M_PI/(GLfloat)( ( n == 0 ) ? 1 : n );
960
961     /* Allocate memory for n samples, plus duplicate of first entry at the end */
962     *sint = malloc(sizeof(GLfloat) * (size+1));
963     *cost = malloc(sizeof(GLfloat) * (size+1));
964
965     /* Bail out if memory allocation fails, fgError never returns */
966     if (!(*sint) || !(*cost))
967     {
968         free(*sint);
969         free(*cost);
970         fgError("Failed to allocate memory in fghCircleTable");
971     }
972
973     /* Compute cos and sin around the circle */
974     (*sint)[0] = 0.0;
975     (*cost)[0] = 1.0;
976
977     for (i=1; i<size; i++)
978     {
979         (*sint)[i] = (GLfloat)sin(angle*i);
980         (*cost)[i] = (GLfloat)cos(angle*i);
981     }
982
983     
984     if (halfCircle)
985     {
986         (*sint)[size] =  0.0f;  /* sin PI */
987         (*cost)[size] = -1.0f;  /* cos PI */
988     }
989     else
990     {
991         /* Last sample is duplicate of the first (sin or cos of 2 PI) */
992         (*sint)[size] = (*sint)[0];
993         (*cost)[size] = (*cost)[0];
994     }
995 }
996
997 static void fghGenerateSphere(GLfloat radius, GLint slices, GLint stacks, GLfloat **vertices, GLfloat **normals, int* nVert)
998 {
999     int i,j;
1000     int idx = 0;    /* idx into vertex/normal buffer */
1001     GLfloat x,y,z;
1002
1003     /* Pre-computed circle */
1004     GLfloat *sint1,*cost1;
1005     GLfloat *sint2,*cost2;
1006
1007     /* number of unique vertices */
1008     if (slices==0 || stacks<2)
1009     {
1010         /* nothing to generate */
1011         *nVert = 0;
1012         return;
1013     }
1014     *nVert = slices*(stacks-1)+2;
1015     if ((*nVert) > 65535)
1016         /*
1017          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1018          * But still:
1019          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1020          */
1021         fgWarning("fghGenerateSphere: too many slices or stacks requested, indices will wrap");
1022
1023     /* precompute values on unit circle */
1024     fghCircleTable(&sint1,&cost1,-slices,FALSE);
1025     fghCircleTable(&sint2,&cost2, stacks,TRUE);
1026
1027     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1028     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1029     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1030     if (!(*vertices) || !(*normals))
1031     {
1032         free(*vertices);
1033         free(*normals);
1034         fgError("Failed to allocate memory in fghGenerateSphere");
1035     }
1036
1037     /* top */
1038     (*vertices)[0] = 0.f;
1039     (*vertices)[1] = 0.f;
1040     (*vertices)[2] = radius;
1041     (*normals )[0] = 0.f;
1042     (*normals )[1] = 0.f;
1043     (*normals )[2] = 1.f;
1044     idx = 3;
1045
1046     /* each stack */
1047     for( i=1; i<stacks; i++ )
1048     {
1049         for(j=0; j<slices; j++, idx+=3)
1050         {
1051             x = cost1[j]*sint2[i];
1052             y = sint1[j]*sint2[i];
1053             z = cost2[i];
1054
1055             (*vertices)[idx  ] = x*radius;
1056             (*vertices)[idx+1] = y*radius;
1057             (*vertices)[idx+2] = z*radius;
1058             (*normals )[idx  ] = x;
1059             (*normals )[idx+1] = y;
1060             (*normals )[idx+2] = z;
1061         }
1062     }
1063
1064     /* bottom */
1065     (*vertices)[idx  ] =  0.f;
1066     (*vertices)[idx+1] =  0.f;
1067     (*vertices)[idx+2] = -radius;
1068     (*normals )[idx  ] =  0.f;
1069     (*normals )[idx+1] =  0.f;
1070     (*normals )[idx+2] = -1.f;
1071
1072     /* Done creating vertices, release sin and cos tables */
1073     free(sint1);
1074     free(cost1);
1075     free(sint2);
1076     free(cost2);
1077 }
1078
1079 void fghGenerateCone(
1080     GLfloat base, GLfloat height, GLint slices, GLint stacks,   /*  input */
1081     GLfloat **vertices, GLfloat **normals, int* nVert           /* output */
1082     )
1083 {
1084     int i,j;
1085     int idx = 0;    /* idx into vertex/normal buffer */
1086
1087     /* Pre-computed circle */
1088     GLfloat *sint,*cost;
1089
1090     /* Step in z and radius as stacks are drawn. */
1091     GLfloat z = 0;
1092     GLfloat r = (GLfloat)base;
1093
1094     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1095     const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
1096
1097     /* Scaling factors for vertex normals */
1098     const GLfloat cosn = (GLfloat) (height / sqrt( height * height + base * base ));
1099     const GLfloat sinn = (GLfloat) (base   / sqrt( height * height + base * base ));
1100
1101
1102
1103     /* number of unique vertices */
1104     if (slices==0 || stacks<1)
1105     {
1106         /* nothing to generate */
1107         *nVert = 0;
1108         return;
1109     }
1110     *nVert = slices*(stacks+2)+1;   /* need an extra stack for closing off bottom with correct normals */
1111
1112     if ((*nVert) > 65535)
1113         /*
1114          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1115          * But still:
1116          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1117          */
1118         fgWarning("fghGenerateCone: too many slices or stacks requested, indices will wrap");
1119
1120     /* Pre-computed circle */
1121     fghCircleTable(&sint,&cost,-slices,FALSE);
1122
1123     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1124     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1125     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1126     if (!(*vertices) || !(*normals))
1127     {
1128         free(*vertices);
1129         free(*normals);
1130         fgError("Failed to allocate memory in fghGenerateCone");
1131     }
1132
1133     /* bottom */
1134     (*vertices)[0] =  0.f;
1135     (*vertices)[1] =  0.f;
1136     (*vertices)[2] =  z;
1137     (*normals )[0] =  0.f;
1138     (*normals )[1] =  0.f;
1139     (*normals )[2] = -1.f;
1140     idx = 3;
1141     /* other on bottom (get normals right) */
1142     for (j=0; j<slices; j++, idx+=3)
1143     {
1144         (*vertices)[idx  ] = cost[j]*r;
1145         (*vertices)[idx+1] = sint[j]*r;
1146         (*vertices)[idx+2] = z;
1147         (*normals )[idx  ] =  0.f;
1148         (*normals )[idx+1] =  0.f;
1149         (*normals )[idx+2] = -1.f;
1150     }
1151
1152     /* each stack */
1153     for (i=0; i<stacks+1; i++ )
1154     {
1155         for (j=0; j<slices; j++, idx+=3)
1156         {
1157             (*vertices)[idx  ] = cost[j]*r;
1158             (*vertices)[idx+1] = sint[j]*r;
1159             (*vertices)[idx+2] = z;
1160             (*normals )[idx  ] = cost[j]*sinn;
1161             (*normals )[idx+1] = sint[j]*sinn;
1162             (*normals )[idx+2] = cosn;
1163         }
1164
1165         z += zStep;
1166         r -= rStep;
1167     }
1168
1169     /* Release sin and cos tables */
1170     free(sint);
1171     free(cost);
1172 }
1173
1174 void fghGenerateCylinder(
1175     GLfloat radius, GLfloat height, GLint slices, GLint stacks, /*  input */
1176     GLfloat **vertices, GLfloat **normals, int* nVert           /* output */
1177     )
1178 {
1179     int i,j;
1180     int idx = 0;    /* idx into vertex/normal buffer */
1181
1182     /* Step in z as stacks are drawn. */
1183     GLfloat radf = (GLfloat)radius;
1184     GLfloat z;
1185     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1186
1187     /* Pre-computed circle */
1188     GLfloat *sint,*cost;
1189
1190     /* number of unique vertices */
1191     if (slices==0 || stacks<1)
1192     {
1193         /* nothing to generate */
1194         *nVert = 0;
1195         return;
1196     }
1197     *nVert = slices*(stacks+3)+2;   /* need two extra stacks for closing off top and bottom with correct normals */
1198
1199     if ((*nVert) > 65535)
1200         /*
1201          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1202          * But still:
1203          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1204          */
1205         fgWarning("fghGenerateCylinder: too many slices or stacks requested, indices will wrap");
1206
1207     /* Pre-computed circle */
1208     fghCircleTable(&sint,&cost,-slices,FALSE);
1209
1210     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1211     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1212     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1213     if (!(*vertices) || !(*normals))
1214     {
1215         free(*vertices);
1216         free(*normals);
1217         fgError("Failed to allocate memory in fghGenerateCylinder");
1218     }
1219
1220     z=0;
1221     /* top on Z-axis */
1222     (*vertices)[0] =  0.f;
1223     (*vertices)[1] =  0.f;
1224     (*vertices)[2] =  0.f;
1225     (*normals )[0] =  0.f;
1226     (*normals )[1] =  0.f;
1227     (*normals )[2] = -1.f;
1228     idx = 3;
1229     /* other on top (get normals right) */
1230     for (j=0; j<slices; j++, idx+=3)
1231     {
1232         (*vertices)[idx  ] = cost[j]*radf;
1233         (*vertices)[idx+1] = sint[j]*radf;
1234         (*vertices)[idx+2] = z;
1235         (*normals )[idx  ] = 0.f;
1236         (*normals )[idx+1] = 0.f;
1237         (*normals )[idx+2] = -1.f;
1238     }
1239
1240     /* each stack */
1241     for (i=0; i<stacks+1; i++ )
1242     {
1243         for (j=0; j<slices; j++, idx+=3)
1244         {
1245             (*vertices)[idx  ] = cost[j]*radf;
1246             (*vertices)[idx+1] = sint[j]*radf;
1247             (*vertices)[idx+2] = z;
1248             (*normals )[idx  ] = cost[j];
1249             (*normals )[idx+1] = sint[j];
1250             (*normals )[idx+2] = 0.f;
1251         }
1252
1253         z += zStep;
1254     }
1255
1256     /* other on bottom (get normals right) */
1257     z -= zStep;
1258     for (j=0; j<slices; j++, idx+=3)
1259     {
1260         (*vertices)[idx  ] = cost[j]*radf;
1261         (*vertices)[idx+1] = sint[j]*radf;
1262         (*vertices)[idx+2] = z;
1263         (*normals )[idx  ] = 0.f;
1264         (*normals )[idx+1] = 0.f;
1265         (*normals )[idx+2] = 1.f;
1266     }
1267
1268     /* bottom */
1269     (*vertices)[idx  ] =  0.f;
1270     (*vertices)[idx+1] =  0.f;
1271     (*vertices)[idx+2] =  height;
1272     (*normals )[idx  ] =  0.f;
1273     (*normals )[idx+1] =  0.f;
1274     (*normals )[idx+2] =  1.f;
1275
1276     /* Release sin and cos tables */
1277     free(sint);
1278     free(cost);
1279 }
1280
1281 void fghGenerateTorus(
1282     double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings, /*  input */
1283     GLfloat **vertices, GLfloat **normals, int* nVert                     /* output */
1284     )
1285 {
1286     GLfloat  iradius = (float)dInnerRadius;
1287     GLfloat  oradius = (float)dOuterRadius;
1288     int    i, j;
1289
1290     /* Pre-computed circle */
1291     GLfloat *spsi, *cpsi;
1292     GLfloat *sphi, *cphi;
1293
1294     /* number of unique vertices */
1295     if (nSides<2 || nRings<2)
1296     {
1297         /* nothing to generate */
1298         *nVert = 0;
1299         return;
1300     }
1301     *nVert = nSides * nRings;
1302
1303     if ((*nVert) > 65535)
1304         /*
1305          * limit of glushort, thats 256*256 subdivisions, should be enough in practice.
1306          * But still:
1307          * TODO: must have a better solution than this low limit, at least for architectures where gluint is available
1308          */
1309         fgWarning("fghGenerateTorus: too many slices or stacks requested, indices will wrap");
1310
1311     /* precompute values on unit circle */
1312     fghCircleTable(&spsi,&cpsi, nRings,FALSE);
1313     fghCircleTable(&sphi,&cphi,-nSides,FALSE);
1314
1315     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1316     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1317     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1318     if (!(*vertices) || !(*normals))
1319     {
1320         free(*vertices);
1321         free(*normals);
1322         fgError("Failed to allocate memory in fghGenerateTorus");
1323     }
1324
1325     for( j=0; j<nRings; j++ )
1326     {
1327         for( i=0; i<nSides; i++ )
1328         {
1329             int offset = 3 * ( j * nSides + i ) ;
1330
1331             (*vertices)[offset  ] = cpsi[j] * ( oradius + cphi[i] * iradius ) ;
1332             (*vertices)[offset+1] = spsi[j] * ( oradius + cphi[i] * iradius ) ;
1333             (*vertices)[offset+2] =                       sphi[i] * iradius  ;
1334             (*normals )[offset  ] = cpsi[j] * cphi[i] ;
1335             (*normals )[offset+1] = spsi[j] * cphi[i] ;
1336             (*normals )[offset+2] =           sphi[i] ;
1337         }
1338     }
1339
1340     /* Release sin and cos tables */
1341     free(spsi);
1342     free(cpsi);
1343     free(sphi);
1344     free(cphi);
1345 }
1346
1347 /* -- INTERNAL DRAWING functions --------------------------------------- */
1348 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
1349     static void fgh##nameICaps( GLboolean useWireMode )\
1350     {\
1351         if (!name##Cached)\
1352         {\
1353             fgh##nameICaps##Generate();\
1354             name##Cached = GL_TRUE;\
1355         }\
1356         \
1357         if (useWireMode)\
1358         {\
1359             fghDrawGeometryWire (name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ, \
1360                                  NULL,nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE,GL_LINE_LOOP,\
1361                                  NULL,0,0);\
1362         }\
1363         else\
1364         {\
1365             fghDrawGeometrySolid(name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ,\
1366                                  vertIdxs, 1, nameCaps##_VERT_PER_OBJ_TRI); \
1367         }\
1368     }
1369 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps)                        _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
1370 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
1371
1372 static void fghCube( GLfloat dSize, GLboolean useWireMode )
1373 {
1374     GLfloat *vertices;
1375
1376     if (!cubeCached)
1377     {
1378         fghCubeGenerate();
1379         cubeCached = GL_TRUE;
1380     }
1381
1382     if (dSize!=1.f)
1383     {
1384         /* Need to build new vertex list containing vertices for cube of different size */
1385         int i;
1386
1387         vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
1388
1389         /* Bail out if memory allocation fails, fgError never returns */
1390         if (!vertices)
1391         {
1392             free(vertices);
1393             fgError("Failed to allocate memory in fghCube");
1394         }
1395
1396         for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
1397             vertices[i] = dSize*cube_verts[i];
1398     }
1399     else
1400         vertices = cube_verts;
1401
1402     if (useWireMode)
1403         fghDrawGeometryWire(vertices, cube_norms, CUBE_VERT_PER_OBJ,
1404                             NULL,CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1405                             NULL,0,0);
1406     else
1407         fghDrawGeometrySolid(vertices, cube_norms, CUBE_VERT_PER_OBJ,
1408                              cube_vertIdxs, 1, CUBE_VERT_PER_OBJ_TRI);
1409
1410     if (dSize!=1.f)
1411         /* cleanup allocated memory */
1412         free(vertices);
1413 }
1414
1415 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
1416 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON)
1417 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON)
1418 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
1419 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON)
1420
1421 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
1422 {
1423     GLfloat *vertices;
1424     GLfloat * normals;
1425     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
1426     GLsizei    numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
1427     GLsizei    numFace = numTetr*TETRAHEDRON_NUM_FACES;
1428
1429     if (numTetr)
1430     {
1431         /* Allocate memory */
1432         vertices = malloc(numVert*3 * sizeof(GLfloat));
1433         normals  = malloc(numVert*3 * sizeof(GLfloat));
1434         /* Bail out if memory allocation fails, fgError never returns */
1435         if (!vertices || !normals)
1436         {
1437             free(vertices);
1438             free(normals);
1439             fgError("Failed to allocate memory in fghSierpinskiSponge");
1440         }
1441
1442         /* Generate elements */
1443         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
1444
1445         /* Draw and cleanup */
1446         if (useWireMode)
1447             fghDrawGeometryWire (vertices,normals,numVert,
1448                                  NULL,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1449                                  NULL,0,0);
1450         else
1451             fghDrawGeometrySolid(vertices,normals,numVert,NULL,1,0);
1452
1453         free(vertices);
1454         free(normals );
1455     }
1456 }
1457
1458
1459 static void fghSphere( GLfloat radius, GLint slices, GLint stacks, GLboolean useWireMode )
1460 {
1461     int i,j,idx, nVert;
1462     GLfloat *vertices, *normals;
1463
1464     /* Generate vertices and normals */
1465     fghGenerateSphere(radius,slices,stacks,&vertices,&normals,&nVert);
1466     
1467     if (nVert==0)
1468         /* nothing to draw */
1469         return;
1470
1471     if (useWireMode)
1472     {
1473         GLushort  *sliceIdx, *stackIdx;
1474         /* First, generate vertex index arrays for drawing with glDrawElements
1475          * We have a bunch of line_loops to draw for each stack, and a
1476          * bunch for each slice.
1477          */
1478
1479         sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1480         stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort));
1481         if (!(stackIdx) || !(sliceIdx))
1482         {
1483             free(stackIdx);
1484             free(sliceIdx);
1485             fgError("Failed to allocate memory in fghSphere");
1486         }
1487
1488         /* generate for each stack */
1489         for (i=0,idx=0; i<stacks-1; i++)
1490         {
1491             GLushort offset = 1+i*slices;           /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1492             for (j=0; j<slices; j++, idx++)
1493             {
1494                 stackIdx[idx] = offset+j;
1495             }
1496         }
1497
1498         /* generate for each slice */
1499         for (i=0,idx=0; i<slices; i++)
1500         {
1501             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1502             sliceIdx[idx++] = 0;                    /* vertex on top */
1503             for (j=0; j<stacks-1; j++, idx++)
1504             {
1505                 sliceIdx[idx] = offset+j*slices;
1506             }
1507             sliceIdx[idx++] = nVert-1;              /* zero based index, last element in array... */
1508         }
1509
1510         /* draw */
1511         fghDrawGeometryWire(vertices,normals,nVert,
1512             sliceIdx,slices,stacks+1,GL_LINE_STRIP,
1513             stackIdx,stacks-1,slices);
1514         
1515         /* cleanup allocated memory */
1516         free(sliceIdx);
1517         free(stackIdx);
1518     }
1519     else
1520     {
1521         /* First, generate vertex index arrays for drawing with glDrawElements
1522          * All stacks, including top and bottom are covered with a triangle
1523          * strip.
1524          */
1525         GLushort  *stripIdx;
1526         /* Create index vector */
1527         GLushort offset;
1528
1529         /* Allocate buffers for indices, bail out if memory allocation fails */
1530         stripIdx = malloc((slices+1)*2*(stacks)*sizeof(GLushort));
1531         if (!(stripIdx))
1532         {
1533             free(stripIdx);
1534             fgError("Failed to allocate memory in fghSphere");
1535         }
1536
1537         /* top stack */
1538         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1539         {
1540             stripIdx[idx  ] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1541             stripIdx[idx+1] = 0;
1542         }
1543         stripIdx[idx  ] = 1;                    /* repeat first slice's idx for closing off shape */
1544         stripIdx[idx+1] = 0;
1545         idx+=2;
1546
1547         /* middle stacks: */
1548         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1549         for (i=0; i<stacks-2; i++, idx+=2)
1550         {
1551             offset = 1+i*slices;                    /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1552             for (j=0; j<slices; j++, idx+=2)
1553             {
1554                 stripIdx[idx  ] = offset+j+slices;
1555                 stripIdx[idx+1] = offset+j;
1556             }
1557             stripIdx[idx  ] = offset+slices;        /* repeat first slice's idx for closing off shape */
1558             stripIdx[idx+1] = offset;
1559         }
1560
1561         /* bottom stack */
1562         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 */
1563         for (j=0; j<slices; j++, idx+=2)
1564         {
1565             stripIdx[idx  ] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1566             stripIdx[idx+1] = offset+j;
1567         }
1568         stripIdx[idx  ] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1569         stripIdx[idx+1] = offset;
1570
1571
1572         /* draw */
1573         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks,(slices+1)*2);
1574
1575         /* cleanup allocated memory */
1576         free(stripIdx);
1577     }
1578     
1579     /* cleanup allocated memory */
1580     free(vertices);
1581     free(normals);
1582 }
1583
1584 static void fghCone( GLfloat base, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1585 {
1586     int i,j,idx, nVert;
1587     GLfloat *vertices, *normals;
1588
1589     /* Generate vertices and normals */
1590     /* Note, (stacks+1)*slices vertices for side of object, slices+1 for top and bottom closures */
1591     fghGenerateCone(base,height,slices,stacks,&vertices,&normals,&nVert);
1592
1593     if (nVert==0)
1594         /* nothing to draw */
1595         return;
1596
1597     if (useWireMode)
1598     {
1599         GLushort  *sliceIdx, *stackIdx;
1600         /* First, generate vertex index arrays for drawing with glDrawElements
1601          * We have a bunch of line_loops to draw for each stack, and a
1602          * bunch for each slice.
1603          */
1604
1605         stackIdx = malloc(slices*stacks*sizeof(GLushort));
1606         sliceIdx = malloc(slices*2     *sizeof(GLushort));
1607         if (!(stackIdx) || !(sliceIdx))
1608         {
1609             free(stackIdx);
1610             free(sliceIdx);
1611             fgError("Failed to allocate memory in fghCone");
1612         }
1613
1614         /* generate for each stack */
1615         for (i=0,idx=0; i<stacks; i++)
1616         {
1617             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1618             for (j=0; j<slices; j++, idx++)
1619             {
1620                 stackIdx[idx] = offset+j;
1621             }
1622         }
1623
1624         /* generate for each slice */
1625         for (i=0,idx=0; i<slices; i++)
1626         {
1627             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1628             sliceIdx[idx++] = offset+slices;
1629             sliceIdx[idx++] = offset+(stacks+1)*slices;
1630         }
1631
1632         /* draw */
1633         fghDrawGeometryWire(vertices,normals,nVert,
1634             sliceIdx,1,slices*2,GL_LINES,
1635             stackIdx,stacks,slices);
1636
1637         /* cleanup allocated memory */
1638         free(sliceIdx);
1639         free(stackIdx);
1640     }
1641     else
1642     {
1643         /* First, generate vertex index arrays for drawing with glDrawElements
1644          * All stacks, including top and bottom are covered with a triangle
1645          * strip.
1646          */
1647         GLushort  *stripIdx;
1648         /* Create index vector */
1649         GLushort offset;
1650
1651         /* Allocate buffers for indices, bail out if memory allocation fails */
1652         stripIdx = malloc((slices+1)*2*(stacks+1)*sizeof(GLushort));    /*stacks +1 because of closing off bottom */
1653         if (!(stripIdx))
1654         {
1655             free(stripIdx);
1656             fgError("Failed to allocate memory in fghCone");
1657         }
1658
1659         /* top stack */
1660         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1661         {
1662             stripIdx[idx  ] = 0;
1663             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1664         }
1665         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1666         stripIdx[idx+1] = 1;
1667         idx+=2;
1668
1669         /* middle stacks: */
1670         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1671         for (i=0; i<stacks; i++, idx+=2)
1672         {
1673             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 */
1674             for (j=0; j<slices; j++, idx+=2)
1675             {
1676                 stripIdx[idx  ] = offset+j;
1677                 stripIdx[idx+1] = offset+j+slices;
1678             }
1679             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1680             stripIdx[idx+1] = offset+slices;
1681         }
1682
1683         /* draw */
1684         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks+1,(slices+1)*2);
1685
1686         /* cleanup allocated memory */
1687         free(stripIdx);
1688     }
1689
1690     /* cleanup allocated memory */
1691     free(vertices);
1692     free(normals);
1693 }
1694
1695 static void fghCylinder( GLfloat radius, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1696 {
1697     int i,j,idx, nVert;
1698     GLfloat *vertices, *normals;
1699
1700     /* Generate vertices and normals */
1701     /* Note, (stacks+1)*slices vertices for side of object, 2*slices+2 for top and bottom closures */
1702     fghGenerateCylinder(radius,height,slices,stacks,&vertices,&normals,&nVert);
1703
1704     if (nVert==0)
1705         /* nothing to draw */
1706         return;
1707
1708     if (useWireMode)
1709     {
1710         GLushort  *sliceIdx, *stackIdx;
1711         /* First, generate vertex index arrays for drawing with glDrawElements
1712          * We have a bunch of line_loops to draw for each stack, and a
1713          * bunch for each slice.
1714          */
1715
1716         stackIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1717         sliceIdx = malloc(slices*2         *sizeof(GLushort));
1718         if (!(stackIdx) || !(sliceIdx))
1719         {
1720             free(stackIdx);
1721             free(sliceIdx);
1722             fgError("Failed to allocate memory in fghCylinder");
1723         }
1724
1725         /* generate for each stack */
1726         for (i=0,idx=0; i<stacks+1; i++)
1727         {
1728             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1729             for (j=0; j<slices; j++, idx++)
1730             {
1731                 stackIdx[idx] = offset+j;
1732             }
1733         }
1734
1735         /* generate for each slice */
1736         for (i=0,idx=0; i<slices; i++)
1737         {
1738             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1739             sliceIdx[idx++] = offset+slices;
1740             sliceIdx[idx++] = offset+(stacks+1)*slices;
1741         }
1742
1743         /* draw */
1744         fghDrawGeometryWire(vertices,normals,nVert,
1745             sliceIdx,1,slices*2,GL_LINES,
1746             stackIdx,stacks+1,slices);
1747
1748         /* cleanup allocated memory */
1749         free(sliceIdx);
1750         free(stackIdx);
1751     }
1752     else
1753     {
1754         /* First, generate vertex index arrays for drawing with glDrawElements
1755          * All stacks, including top and bottom are covered with a triangle
1756          * strip.
1757          */
1758         GLushort  *stripIdx;
1759         /* Create index vector */
1760         GLushort offset;
1761
1762         /* Allocate buffers for indices, bail out if memory allocation fails */
1763         stripIdx = malloc((slices+1)*2*(stacks+2)*sizeof(GLushort));    /*stacks +2 because of closing off bottom and top */
1764         if (!(stripIdx))
1765         {
1766             free(stripIdx);
1767             fgError("Failed to allocate memory in fghCylinder");
1768         }
1769
1770         /* top stack */
1771         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1772         {
1773             stripIdx[idx  ] = 0;
1774             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1775         }
1776         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1777         stripIdx[idx+1] = 1;
1778         idx+=2;
1779
1780         /* middle stacks: */
1781         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1782         for (i=0; i<stacks; i++, idx+=2)
1783         {
1784             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 */
1785             for (j=0; j<slices; j++, idx+=2)
1786             {
1787                 stripIdx[idx  ] = offset+j;
1788                 stripIdx[idx+1] = offset+j+slices;
1789             }
1790             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1791             stripIdx[idx+1] = offset+slices;
1792         }
1793
1794         /* top stack */
1795         offset = 1+(stacks+2)*slices;
1796         for (j=0; j<slices; j++, idx+=2)
1797         {
1798             stripIdx[idx  ] = offset+j;
1799             stripIdx[idx+1] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1800         }
1801         stripIdx[idx  ] = offset;
1802         stripIdx[idx+1] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1803
1804         /* draw */
1805         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks+2,(slices+1)*2);
1806
1807         /* cleanup allocated memory */
1808         free(stripIdx);
1809     }
1810
1811     /* cleanup allocated memory */
1812     free(vertices);
1813     free(normals);
1814 }
1815
1816 static void fghTorus( GLfloat dInnerRadius, GLfloat dOuterRadius, GLint nSides, GLint nRings, GLboolean useWireMode )
1817 {
1818     int i,j,idx, nVert;
1819     GLfloat *vertices, *normals;
1820
1821     /* Generate vertices and normals */
1822     fghGenerateTorus(dInnerRadius,dOuterRadius,nSides,nRings, &vertices,&normals,&nVert);
1823
1824     if (nVert==0)
1825         /* nothing to draw */
1826         return;
1827
1828     if (useWireMode)
1829     {
1830         GLushort  *sideIdx, *ringIdx;
1831         /* First, generate vertex index arrays for drawing with glDrawElements
1832          * We have a bunch of line_loops to draw each side, and a
1833          * bunch for each ring.
1834          */
1835
1836         ringIdx = malloc(nRings*nSides*sizeof(GLushort));
1837         sideIdx = malloc(nSides*nRings*sizeof(GLushort));
1838         if (!(ringIdx) || !(sideIdx))
1839         {
1840             free(ringIdx);
1841             free(sideIdx);
1842             fgError("Failed to allocate memory in fghTorus");
1843         }
1844
1845         /* generate for each ring */
1846         for( j=0,idx=0; j<nRings; j++ )
1847             for( i=0; i<nSides; i++, idx++ )
1848                 ringIdx[idx] = j * nSides + i;
1849
1850         /* generate for each side */
1851         for( i=0,idx=0; i<nSides; i++ )
1852             for( j=0; j<nRings; j++, idx++ )
1853                 sideIdx[idx] = j * nSides + i;
1854
1855         /* draw */
1856         fghDrawGeometryWire(vertices,normals,nVert,
1857             ringIdx,nRings,nSides,GL_LINE_LOOP,
1858             sideIdx,nSides,nRings);
1859         
1860         /* cleanup allocated memory */
1861         free(sideIdx);
1862         free(ringIdx);
1863     }
1864     else
1865     {
1866         /* First, generate vertex index arrays for drawing with glDrawElements
1867          * All stacks, including top and bottom are covered with a triangle
1868          * strip.
1869          */
1870         GLushort  *stripIdx;
1871
1872         /* Allocate buffers for indices, bail out if memory allocation fails */
1873         stripIdx = malloc((nRings+1)*2*nSides*sizeof(GLushort));
1874         if (!(stripIdx))
1875         {
1876             free(stripIdx);
1877             fgError("Failed to allocate memory in fghTorus");
1878         }
1879
1880         for( i=0, idx=0; i<nSides; i++ )
1881         {
1882             int ioff = 1;
1883             if (i==nSides-1)
1884                 ioff = -i;
1885
1886             for( j=0; j<nRings; j++, idx+=2 )
1887             {
1888                 int offset = j * nSides + i;
1889                 stripIdx[idx  ] = offset;
1890                 stripIdx[idx+1] = offset + ioff;
1891             }
1892             /* repeat first to close off shape */
1893             stripIdx[idx  ] = i;
1894             stripIdx[idx+1] = i + ioff;
1895             idx +=2;
1896         }
1897
1898         /* draw */
1899         fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,nSides,(nRings+1)*2);
1900
1901         /* cleanup allocated memory */
1902         free(stripIdx);
1903     }
1904
1905     /* cleanup allocated memory */
1906     free(vertices);
1907     free(normals);
1908 }
1909
1910
1911 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
1912
1913
1914 /*
1915  * Draws a solid sphere
1916  */
1917 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
1918 {
1919     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
1920     fghSphere((GLfloat)radius, slices, stacks, FALSE );
1921 }
1922
1923 /*
1924  * Draws a wire sphere
1925  */
1926 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
1927 {
1928     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
1929     fghSphere((GLfloat)radius, slices, stacks, TRUE );
1930     
1931 }
1932
1933 /*
1934  * Draws a solid cone
1935  */
1936 void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks )
1937 {
1938     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
1939     fghCone((GLfloat)base, (GLfloat)height, slices, stacks, FALSE );
1940 }
1941
1942 /*
1943  * Draws a wire cone
1944  */
1945 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
1946 {
1947     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1948     fghCone((GLfloat)base, (GLfloat)height, slices, stacks, TRUE );
1949 }
1950
1951
1952 /*
1953  * Draws a solid cylinder
1954  */
1955 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
1956 {
1957     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1958     fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, FALSE );
1959 }
1960
1961 /*
1962  * Draws a wire cylinder
1963  */
1964 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
1965 {
1966     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1967     fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, TRUE );
1968 }
1969
1970 /*
1971  * Draws a wire torus
1972  */
1973 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1974 {
1975     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1976     fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, TRUE);
1977 }
1978
1979 /*
1980  * Draws a solid torus
1981  */
1982 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1983 {
1984     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1985     fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, FALSE);
1986 }
1987
1988
1989
1990 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1991 /* Macro to generate interface functions */
1992 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1993     void FGAPIENTRY glutWire##nameICaps( void )\
1994     {\
1995         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1996         fgh##nameICaps( TRUE );\
1997     }\
1998     void FGAPIENTRY glutSolid##nameICaps( void )\
1999     {\
2000         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
2001         fgh##nameICaps( FALSE );\
2002     }
2003
2004 void FGAPIENTRY glutWireCube( double dSize )
2005 {
2006     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
2007     fghCube( (GLfloat)dSize, TRUE );
2008 }
2009 void FGAPIENTRY glutSolidCube( double dSize )
2010 {
2011     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
2012     fghCube( (GLfloat)dSize, FALSE );
2013 }
2014
2015 DECLARE_SHAPE_INTERFACE(Dodecahedron)
2016 DECLARE_SHAPE_INTERFACE(Icosahedron)
2017 DECLARE_SHAPE_INTERFACE(Octahedron)
2018 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron)
2019
2020 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
2021 {
2022     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
2023     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, TRUE );
2024 }
2025 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
2026 {
2027     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
2028     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, FALSE );
2029 }
2030
2031 DECLARE_SHAPE_INTERFACE(Tetrahedron)
2032
2033
2034 /*** END OF FILE ***/