f4f505689140b281b64e0d38d5d6f8176ba1b865
[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 /* VC++6 in C mode doesn't have C99's sinf/cos/sqrtf */
34 #ifndef HAVE_SINF
35 #define sinf(x) (float)sin((double)(x))
36 #endif
37 #ifndef HAVE_COSF
38 #define cosf(x) (float)cos((double)(x))
39 #endif
40 #ifndef HAVE_SQRTF
41 #define sqrtf(x) (float)sqrt((double)(x))
42 #endif
43
44 /* declare for drawing using the different OpenGL versions here so we can
45    have a nice code order below */
46 #ifndef GL_ES_VERSION_2_0
47 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
48     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
49     GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
50     );
51 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
52     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
53 #endif
54 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
55     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
56     GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
57     GLint attribute_v_coord, GLint attribute_v_normal
58     );
59 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
60     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
61     GLint attribute_v_coord, GLint attribute_v_normal);
62
63 /* Drawing geometry:
64  * Explanation of the functions has to be separate for the polyhedra and
65  * the non-polyhedra (objects with a circular cross-section).
66  * Polyhedra:
67  *   - We have only implemented the five platonic solids and the rhomboid
68  *     dodecahedron. If you need more types of polyhedra, please see
69  *     CPolyhedron in MRPT
70  *   - Solids are drawn by glDrawArrays if composed of triangular faces
71  *     (the tetrahedron, octahedron, and icosahedron), or are first
72  *     decomposed into triangles and then drawn by glDrawElements if its
73  *     faces are squares or pentagons (cube, dodecahedron and rhombic
74  *     dodecahedron) as some vertices are repeated in that case.
75  *   - WireFrame drawing is done using a GL_LINE_LOOP per face, and thus
76  *     issuing one draw call per face. glDrawArrays is always used as no
77  *     triangle decomposition is needed to draw faces. We use the "first"
78  *     parameter in glDrawArrays to go from face to face.
79  * 
80  * Non-polyhedra:
81  *   - We have implemented the sphere, cylinder, cone and torus.
82  *   - All shapes are characterized by two parameters: the number of
83  *     subdivisions along two axes used to construct the shape's vertices 
84  *     (e.g. stacks and slices for the sphere).
85  *     As different subdivisions are most suitable for different shapes,
86  *     and are thus also named differently, I wont provide general comments
87  *     on them here.
88  *   - Solids are drawn using glDrawArrays and GL_TRIANGLE_STRIP. Each
89  *     strip covers one revolution around one of the two subdivision axes
90  *     of the shape.
91  *   - WireFrame drawing is done for the subdivisions along the two axes 
92  *     separately, usually using GL_LINE_LOOP. Vertex index arrays are
93  *     built containing the vertices to be drawn for each loop, which are
94  *     then drawn using multiple calls to glDrawElements. As the number of
95  *     subdivisions along the two axes is not guaranteed to be equal, the
96  *     vertex indices for e.g. stacks and slices are stored in separate
97  *     arrays, which makes the input to the drawing function a bit clunky,
98  *     but allows for the same drawing function to be used for all shapes.
99  */
100
101
102 /**
103  * Draw geometric shape in wire mode (only edges)
104  *
105  * Arguments:
106  * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
107  *   The vertex coordinate and normal buffers, and the number of entries in
108  *   those
109  * GLushort *vertIdxs
110  *   a vertex indices buffer, optional (never passed for the polyhedra)
111  * GLsizei numParts, GLsizei numVertPerPart
112  *   polyhedra: number of faces, and the number of vertices for drawing
113  *     each face
114  *   non-polyhedra: number of edges to draw for first subdivision (not
115  *     necessarily equal to number of subdivisions requested by user, e.g.
116  *     as each subdivision is enclosed by two edges), and number of
117  *     vertices for drawing each
118  *   numParts * numVertPerPart gives the number of entries in the vertex
119  *     array vertIdxs
120  * GLenum vertexMode
121  *   vertex drawing mode (e.g. always GL_LINE_LOOP for polyhedra, varies
122  *   for others)
123  * GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
124  *   non-polyhedra only: same as the above, but now for subdivisions along
125  *   the other axis. Always drawn as GL_LINE_LOOP.
126  *
127  * Feel free to contribute better naming ;)
128  */
129 static void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
130     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
131     GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
132     )
133 {
134     GLint attribute_v_coord  = fgStructure.CurrentWindow->Window.attribute_v_coord;
135     GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal;
136
137     if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
138         /* User requested a 2.0 draw */
139         fghDrawGeometryWire20(vertices, normals, numVertices,
140                               vertIdxs, numParts, numVertPerPart, vertexMode,
141                               vertIdxs2, numParts2, numVertPerPart2,
142                               attribute_v_coord, attribute_v_normal);
143 #ifndef GL_ES_VERSION_2_0
144     else
145         fghDrawGeometryWire11(vertices, normals,
146                               vertIdxs, numParts, numVertPerPart, vertexMode,
147                               vertIdxs2, numParts2, numVertPerPart2);
148 #endif
149 }
150
151 /* Draw the geometric shape with filled triangles
152  *
153  * Arguments:
154  * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
155  *   The vertex coordinate and normal buffers, and the number of entries in
156  *   those
157  * GLushort *vertIdxs
158  *   a vertex indices buffer, optional (not passed for the polyhedra with
159  *   triangular faces)
160  * GLsizei numParts, GLsizei numVertPerPart
161  *   polyhedra: not used for polyhedra with triangular faces
162        (numEdgePerFace==3), as each vertex+normal pair is drawn only once,
163        so no vertex indices are used.
164        Else, the shape was triangulated (DECOMPOSE_TO_TRIANGLE), leading to
165        reuse of some vertex+normal pairs, and thus the need to draw with
166        glDrawElements. numParts is always 1 in this case (we can draw the
167        whole object with one call to glDrawElements as the vertex index
168        array contains separate triangles), and numVertPerPart indicates
169        the number of vertex indices in the vertex array.
170  *   non-polyhedra: number of parts (GL_TRIANGLE_STRIPs) to be drawn
171        separately (numParts calls to glDrawElements) to create the object.
172        numVertPerPart indicates the number of vertex indices to be
173        processed at each draw call.
174  *   numParts * numVertPerPart gives the number of entries in the vertex
175  *     array vertIdxs
176  */
177 static void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
178                                  GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
179 {
180     GLint attribute_v_coord  = fgStructure.CurrentWindow->Window.attribute_v_coord;
181     GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal;
182
183     if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
184         /* User requested a 2.0 draw */
185         fghDrawGeometrySolid20(vertices, normals, numVertices,
186                                vertIdxs, numParts, numVertIdxsPerPart,
187                                attribute_v_coord, attribute_v_normal);
188 #ifndef GL_ES_VERSION_2_0
189     else
190         fghDrawGeometrySolid11(vertices, normals, numVertices,
191                                vertIdxs, numParts, numVertIdxsPerPart);
192 #endif
193 }
194
195
196
197 /* Version for OpenGL (ES) 1.1 */
198 #ifndef GL_ES_VERSION_2_0
199 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
200     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
201     GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
202     )
203 {
204     int i;
205     
206     glEnableClientState(GL_VERTEX_ARRAY);
207     glEnableClientState(GL_NORMAL_ARRAY);
208
209     glVertexPointer(3, GL_FLOAT, 0, vertices);
210     glNormalPointer(GL_FLOAT, 0, normals);
211
212     
213     if (!vertIdxs)
214         /* Draw per face (TODO: could use glMultiDrawArrays if available) */
215         for (i=0; i<numParts; i++)
216             glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
217     else
218         for (i=0; i<numParts; i++)
219             glDrawElements(vertexMode,numVertPerPart,GL_UNSIGNED_SHORT,vertIdxs+i*numVertPerPart);
220
221     if (vertIdxs2)
222         for (i=0; i<numParts2; i++)
223             glDrawElements(GL_LINE_LOOP,numVertPerPart2,GL_UNSIGNED_SHORT,vertIdxs2+i*numVertPerPart2);
224
225     glDisableClientState(GL_VERTEX_ARRAY);
226     glDisableClientState(GL_NORMAL_ARRAY);
227 }
228
229
230 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
231     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
232 {
233     int i;
234
235     glEnableClientState(GL_VERTEX_ARRAY);
236     glEnableClientState(GL_NORMAL_ARRAY);
237
238     glVertexPointer(3, GL_FLOAT, 0, vertices);
239     glNormalPointer(GL_FLOAT, 0, normals);
240
241     if (!vertIdxs)
242         glDrawArrays(GL_TRIANGLES, 0, numVertices);
243     else
244         if (numParts>1)
245             for (i=0; i<numParts; i++)
246                 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs+i*numVertIdxsPerPart);
247         else
248             glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs);
249
250     glDisableClientState(GL_VERTEX_ARRAY);
251     glDisableClientState(GL_NORMAL_ARRAY);
252 }
253 #endif
254
255 /* Version for OpenGL (ES) >= 2.0 */
256 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
257     GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
258     GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
259     GLint attribute_v_coord, GLint attribute_v_normal
260     )
261 {
262     GLuint vbo_coords = 0, vbo_normals = 0,
263         ibo_elements = 0, ibo_elements2 = 0;
264     GLsizei numVertIdxs = numParts * numVertPerPart;
265     GLsizei numVertIdxs2 = numParts2 * numVertPerPart2;
266     int i;
267
268     if (numVertices > 0 && attribute_v_coord != -1) {
269         fghGenBuffers(1, &vbo_coords);
270         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
271         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
272                       vertices, FGH_STATIC_DRAW);
273     }
274     
275     if (numVertices > 0 && attribute_v_normal != -1) {
276         fghGenBuffers(1, &vbo_normals);
277         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
278         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
279                       normals, FGH_STATIC_DRAW);
280     }
281     
282     if (vertIdxs != NULL) {
283         fghGenBuffers(1, &ibo_elements);
284         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
285         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
286                       vertIdxs, FGH_STATIC_DRAW);
287         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
288     }
289
290     if (vertIdxs2 != NULL) {
291         fghGenBuffers(1, &ibo_elements2);
292         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
293         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs2 * sizeof(vertIdxs2[0]),
294                       vertIdxs2, FGH_STATIC_DRAW);
295         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
296     }
297
298     if (vbo_coords) {
299         fghEnableVertexAttribArray(attribute_v_coord);
300         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
301         fghVertexAttribPointer(
302             attribute_v_coord,  /* attribute */
303             3,                  /* number of elements per vertex, here (x,y,z) */
304             GL_FLOAT,           /* the type of each element */
305             GL_FALSE,           /* take our values as-is */
306             0,                  /* no extra data between each position */
307             0                   /* offset of first element */
308         );
309         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
310     }
311
312     if (vbo_normals) {
313         fghEnableVertexAttribArray(attribute_v_normal);
314         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
315         fghVertexAttribPointer(
316             attribute_v_normal, /* attribute */
317             3,                  /* number of elements per vertex, here (x,y,z) */
318             GL_FLOAT,           /* the type of each element */
319             GL_FALSE,           /* take our values as-is */
320             0,                  /* no extra data between each position */
321             0                   /* offset of first element */
322         );
323         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
324     }
325
326     if (!vertIdxs) {
327         /* Draw per face (TODO: could use glMultiDrawArrays if available) */
328         for (i=0; i<numParts; i++)
329             glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
330     } else {
331         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
332         for (i=0; i<numParts; i++)
333             glDrawElements(vertexMode, numVertPerPart,
334                            GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertPerPart));
335         /* Clean existing bindings before clean-up */
336         /* Android showed instability otherwise */
337         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
338     }
339
340     if (vertIdxs2) {
341         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
342         for (i=0; i<numParts2; i++)
343             glDrawElements(GL_LINE_LOOP, numVertPerPart2,
344                            GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs2[0])*i*numVertPerPart2));
345         /* Clean existing bindings before clean-up */
346         /* Android showed instability otherwise */
347         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
348     }
349     
350     if (vbo_coords != 0)
351         fghDisableVertexAttribArray(attribute_v_coord);
352     if (vbo_normals != 0)
353         fghDisableVertexAttribArray(attribute_v_normal);
354     
355     if (vbo_coords != 0)
356         fghDeleteBuffers(1, &vbo_coords);
357     if (vbo_normals != 0)
358         fghDeleteBuffers(1, &vbo_normals);
359     if (ibo_elements != 0)
360         fghDeleteBuffers(1, &ibo_elements);
361     if (ibo_elements2 != 0)
362         fghDeleteBuffers(1, &ibo_elements2);
363 }
364
365
366
367
368 /* Version for OpenGL (ES) >= 2.0 */
369 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
370                                    GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
371                                    GLint attribute_v_coord, GLint attribute_v_normal)
372 {
373     GLuint vbo_coords = 0, vbo_normals = 0, ibo_elements = 0;
374     GLsizei numVertIdxs = numParts * numVertIdxsPerPart;
375     int i;
376   
377     if (numVertices > 0 && attribute_v_coord != -1) {
378         fghGenBuffers(1, &vbo_coords);
379         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
380         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
381                       vertices, FGH_STATIC_DRAW);
382         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
383     }
384     
385     if (numVertices > 0 && attribute_v_normal != -1) {
386         fghGenBuffers(1, &vbo_normals);
387         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
388         fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
389                       normals, FGH_STATIC_DRAW);
390         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
391     }
392     
393     if (vertIdxs != NULL) {
394         fghGenBuffers(1, &ibo_elements);
395         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
396         fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
397                       vertIdxs, FGH_STATIC_DRAW);
398         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
399     }
400     
401     if (vbo_coords) {
402         fghEnableVertexAttribArray(attribute_v_coord);
403         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
404         fghVertexAttribPointer(
405             attribute_v_coord,  /* attribute */
406             3,                  /* number of elements per vertex, here (x,y,z) */
407             GL_FLOAT,           /* the type of each element */
408             GL_FALSE,           /* take our values as-is */
409             0,                  /* no extra data between each position */
410             0                   /* offset of first element */
411         );
412         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
413     };
414     
415     if (vbo_normals) {
416         fghEnableVertexAttribArray(attribute_v_normal);
417         fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
418         fghVertexAttribPointer(
419             attribute_v_normal, /* attribute */
420             3,                  /* number of elements per vertex, here (x,y,z) */
421             GL_FLOAT,           /* the type of each element */
422             GL_FALSE,           /* take our values as-is */
423             0,                  /* no extra data between each position */
424             0                   /* offset of first element */
425         );
426         fghBindBuffer(FGH_ARRAY_BUFFER, 0);
427     };
428     
429     if (vertIdxs == NULL) {
430         glDrawArrays(GL_TRIANGLES, 0, numVertices);
431     } else {
432         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
433         if (numParts>1) {
434             for (i=0; i<numParts; i++) {
435                 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertIdxsPerPart));
436             }
437         } else {
438             glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, 0);
439         }
440         /* Clean existing bindings before clean-up */
441         /* Android showed instability otherwise */
442         fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
443     }
444     
445     if (vbo_coords != 0)
446         fghDisableVertexAttribArray(attribute_v_coord);
447     if (vbo_normals != 0)
448         fghDisableVertexAttribArray(attribute_v_normal);
449     
450     if (vbo_coords != 0)
451         fghDeleteBuffers(1, &vbo_coords);
452     if (vbo_normals != 0)
453         fghDeleteBuffers(1, &vbo_normals);
454     if (ibo_elements != 0)
455         fghDeleteBuffers(1, &ibo_elements);
456 }
457
458
459
460 /**
461  * Generate all combinations of vertices and normals needed to draw object.
462  * Optional shape decomposition to triangles:
463  * We'll use glDrawElements to draw all shapes that are not naturally
464  * composed of triangles, so generate an index vector here, using the
465  * below sampling scheme.
466  * Be careful to keep winding of all triangles counter-clockwise,
467  * assuming that input has correct winding...
468  */
469 static GLubyte   vert4Decomp[6] = {0,1,2, 0,2,3};             /* quad    : 4 input vertices, 6 output (2 triangles) */
470 static GLubyte   vert5Decomp[9] = {0,1,2, 0,2,4, 4,2,3};      /* pentagon: 5 input vertices, 9 output (3 triangles) */
471
472 static void fghGenerateGeometryWithIndexArray(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut, GLushort *vertIdxOut)
473 {
474     int i,j,numEdgeIdxPerFace;
475     GLubyte   *vertSamps = NULL;
476     switch (numEdgePerFace)
477     {
478     case 3:
479         /* nothing to do here, we'll draw with glDrawArrays */
480         break;
481     case 4:
482         vertSamps = vert4Decomp;
483         numEdgeIdxPerFace = 6;      /* 6 output vertices for each face */
484         break;
485     case 5:
486         vertSamps = vert5Decomp;
487         numEdgeIdxPerFace = 9;      /* 9 output vertices for each face */
488         break;
489     }
490     /*
491      * Build array with vertices using vertex coordinates and vertex indices
492      * Do same for normals.
493      * Need to do this because of different normals at shared vertices.
494      */
495     for (i=0; i<numFaces; i++)
496     {
497         int normIdx         = i*3;
498         int faceIdxVertIdx  = i*numEdgePerFace; /* index to first element of "row" in vertex indices */
499         for (j=0; j<numEdgePerFace; j++)
500         {
501             int outIdx  = i*numEdgePerFace*3+j*3;
502             int vertIdx = vertIndices[faceIdxVertIdx+j]*3;
503
504             vertOut[outIdx  ] = vertices[vertIdx  ];
505             vertOut[outIdx+1] = vertices[vertIdx+1];
506             vertOut[outIdx+2] = vertices[vertIdx+2];
507
508             normOut[outIdx  ] = normals [normIdx  ];
509             normOut[outIdx+1] = normals [normIdx+1];
510             normOut[outIdx+2] = normals [normIdx+2];
511         }
512
513         /* generate vertex indices for each face */
514         if (vertSamps)
515             for (j=0; j<numEdgeIdxPerFace; j++)
516                 vertIdxOut[i*numEdgeIdxPerFace+j] = faceIdxVertIdx + vertSamps[j];
517     }
518 }
519
520 static void fghGenerateGeometry(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut)
521 {
522     /* This function does the same as fghGenerateGeometryWithIndexArray, just skipping the index array generation... */
523     fghGenerateGeometryWithIndexArray(numFaces, numEdgePerFace, vertices, vertIndices, normals, vertOut, normOut, NULL);
524 }
525
526
527 /* -- INTERNAL SETUP OF GEOMETRY --------------------------------------- */
528 /* -- stuff that can be cached -- */
529 /* Cache of input to glDrawArrays or glDrawElements
530  * In general, we build arrays with all vertices or normals.
531  * We cant compress this and use glDrawElements as all combinations of
532  * vertices and normals are unique.
533  */
534 #define DECLARE_SHAPE_CACHE(name,nameICaps,nameCaps)\
535     static GLboolean name##Cached = FALSE;\
536     static GLfloat   name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
537     static GLfloat   name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
538     static void fgh##nameICaps##Generate()\
539     {\
540         fghGenerateGeometry(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
541                             name##_v, name##_vi, name##_n,\
542                             name##_verts, name##_norms);\
543     }
544 #define DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(name,nameICaps,nameCaps)\
545     static GLboolean name##Cached = FALSE;\
546     static GLfloat   name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
547     static GLfloat   name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
548     static GLushort  name##_vertIdxs[nameCaps##_VERT_PER_OBJ_TRI];\
549     static void fgh##nameICaps##Generate()\
550     {\
551         fghGenerateGeometryWithIndexArray(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
552                                           name##_v, name##_vi, name##_n,\
553                                           name##_verts, name##_norms, name##_vertIdxs);\
554     }
555
556 /* -- Cube -- */
557 #define CUBE_NUM_VERT           8
558 #define CUBE_NUM_FACES          6
559 #define CUBE_NUM_EDGE_PER_FACE  4
560 #define CUBE_VERT_PER_OBJ       (CUBE_NUM_FACES*CUBE_NUM_EDGE_PER_FACE)
561 #define CUBE_VERT_ELEM_PER_OBJ  (CUBE_VERT_PER_OBJ*3)
562 #define CUBE_VERT_PER_OBJ_TRI   (CUBE_VERT_PER_OBJ+CUBE_NUM_FACES*2)    /* 2 extra edges per face when drawing quads as triangles */
563 /* Vertex Coordinates */
564 static GLfloat cube_v[CUBE_NUM_VERT*3] =
565 {
566      .5f, .5f, .5f,
567     -.5f, .5f, .5f,
568     -.5f,-.5f, .5f,
569      .5f,-.5f, .5f,
570      .5f,-.5f,-.5f,
571      .5f, .5f,-.5f,
572     -.5f, .5f,-.5f,
573     -.5f,-.5f,-.5f
574 };
575 /* Normal Vectors */
576 static GLfloat cube_n[CUBE_NUM_FACES*3] =
577 {
578      0.0f, 0.0f, 1.0f,
579      1.0f, 0.0f, 0.0f,
580      0.0f, 1.0f, 0.0f,
581     -1.0f, 0.0f, 0.0f,
582      0.0f,-1.0f, 0.0f,
583      0.0f, 0.0f,-1.0f
584 };
585
586 /* Vertex indices, as quads, before triangulation */
587 static GLubyte cube_vi[CUBE_VERT_PER_OBJ] =
588 {
589     0,1,2,3,
590     0,3,4,5,
591     0,5,6,1,
592     1,6,7,2,
593     7,4,3,2,
594     4,7,6,5
595 };
596 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(cube,Cube,CUBE)
597
598 /* -- Dodecahedron -- */
599 /* Magic Numbers:  It is possible to create a dodecahedron by attaching two
600  * pentagons to each face of of a cube. The coordinates of the points are:
601  *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
602  * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
603  *       x = 0.61803398875 and z = 1.61803398875.
604  */
605 #define DODECAHEDRON_NUM_VERT           20
606 #define DODECAHEDRON_NUM_FACES          12
607 #define DODECAHEDRON_NUM_EDGE_PER_FACE  5
608 #define DODECAHEDRON_VERT_PER_OBJ       (DODECAHEDRON_NUM_FACES*DODECAHEDRON_NUM_EDGE_PER_FACE)
609 #define DODECAHEDRON_VERT_ELEM_PER_OBJ  (DODECAHEDRON_VERT_PER_OBJ*3)
610 #define DODECAHEDRON_VERT_PER_OBJ_TRI   (DODECAHEDRON_VERT_PER_OBJ+DODECAHEDRON_NUM_FACES*4)    /* 4 extra edges per face when drawing pentagons as triangles */
611 /* Vertex Coordinates */
612 static GLfloat dodecahedron_v[DODECAHEDRON_NUM_VERT*3] =
613 {
614                0.0f,  1.61803398875f,  0.61803398875f,
615     -          1.0f,            1.0f,            1.0f,
616     -0.61803398875f,            0.0f,  1.61803398875f,
617      0.61803398875f,            0.0f,  1.61803398875f,
618                1.0f,            1.0f,            1.0f,
619                0.0f,  1.61803398875f, -0.61803398875f,
620                1.0f,            1.0f, -          1.0f,
621      0.61803398875f,            0.0f, -1.61803398875f,
622     -0.61803398875f,            0.0f, -1.61803398875f,
623     -          1.0f,            1.0f, -          1.0f,
624                0.0f, -1.61803398875f,  0.61803398875f,
625                1.0f, -          1.0f,            1.0f,
626     -          1.0f, -          1.0f,            1.0f,
627                0.0f, -1.61803398875f, -0.61803398875f,
628     -          1.0f, -          1.0f, -          1.0f,
629                1.0f, -          1.0f, -          1.0f,
630      1.61803398875f, -0.61803398875f,            0.0f,
631      1.61803398875f,  0.61803398875f,            0.0f,
632     -1.61803398875f,  0.61803398875f,            0.0f,
633     -1.61803398875f, -0.61803398875f,            0.0f
634 };
635 /* Normal Vectors */
636 static GLfloat dodecahedron_n[DODECAHEDRON_NUM_FACES*3] =
637 {
638                 0.0f,  0.525731112119f,  0.850650808354f,
639                 0.0f,  0.525731112119f, -0.850650808354f,
640                 0.0f, -0.525731112119f,  0.850650808354f,
641                 0.0f, -0.525731112119f, -0.850650808354f,
642
643      0.850650808354f,             0.0f,  0.525731112119f,
644     -0.850650808354f,             0.0f,  0.525731112119f,
645      0.850650808354f,             0.0f, -0.525731112119f,
646     -0.850650808354f,             0.0f, -0.525731112119f,
647
648      0.525731112119f,  0.850650808354f,             0.0f,
649      0.525731112119f, -0.850650808354f,             0.0f,
650     -0.525731112119f,  0.850650808354f,             0.0f, 
651     -0.525731112119f, -0.850650808354f,             0.0f,
652 };
653
654 /* Vertex indices */
655 static GLubyte dodecahedron_vi[DODECAHEDRON_VERT_PER_OBJ] =
656 {
657      0,  1,  2,  3,  4, 
658      5,  6,  7,  8,  9, 
659     10, 11,  3,  2, 12, 
660     13, 14,  8,  7, 15, 
661
662      3, 11, 16, 17,  4, 
663      2,  1, 18, 19, 12, 
664      7,  6, 17, 16, 15, 
665      8, 14, 19, 18,  9, 
666
667     17,  6,  5,  0,  4, 
668     16, 11, 10, 13, 15, 
669     18,  1,  0,  5,  9, 
670     19, 14, 13, 10, 12
671 };
672 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
673
674
675 /* -- Icosahedron -- */
676 #define ICOSAHEDRON_NUM_VERT           12
677 #define ICOSAHEDRON_NUM_FACES          20
678 #define ICOSAHEDRON_NUM_EDGE_PER_FACE  3
679 #define ICOSAHEDRON_VERT_PER_OBJ       (ICOSAHEDRON_NUM_FACES*ICOSAHEDRON_NUM_EDGE_PER_FACE)
680 #define ICOSAHEDRON_VERT_ELEM_PER_OBJ  (ICOSAHEDRON_VERT_PER_OBJ*3)
681 #define ICOSAHEDRON_VERT_PER_OBJ_TRI   ICOSAHEDRON_VERT_PER_OBJ
682 /* Vertex Coordinates */
683 static GLfloat icosahedron_v[ICOSAHEDRON_NUM_VERT*3] =
684 {
685                 1.0f,             0.0f,             0.0f,
686      0.447213595500f,  0.894427191000f,             0.0f,
687      0.447213595500f,  0.276393202252f,  0.850650808354f,
688      0.447213595500f, -0.723606797748f,  0.525731112119f,
689      0.447213595500f, -0.723606797748f, -0.525731112119f,
690      0.447213595500f,  0.276393202252f, -0.850650808354f,
691     -0.447213595500f, -0.894427191000f,             0.0f,
692     -0.447213595500f, -0.276393202252f,  0.850650808354f,
693     -0.447213595500f,  0.723606797748f,  0.525731112119f,
694     -0.447213595500f,  0.723606797748f, -0.525731112119f,
695     -0.447213595500f, -0.276393202252f, -0.850650808354f,
696     -           1.0f,             0.0f,             0.0f
697 };
698 /* Normal Vectors:
699  * 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] ) ;
700  * 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] ) ;
701  * 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] ) ;
702 */
703 static GLfloat icosahedron_n[ICOSAHEDRON_NUM_FACES*3] =
704 {
705      0.760845213037948f,  0.470228201835026f,  0.341640786498800f,
706      0.760845213036861f, -0.179611190632978f,  0.552786404500000f,
707      0.760845213033849f, -0.581234022404097f,                0.0f,
708      0.760845213036861f, -0.179611190632978f, -0.552786404500000f,
709      0.760845213037948f,  0.470228201835026f, -0.341640786498800f,
710      0.179611190628666f,  0.760845213037948f,  0.552786404498399f,
711      0.179611190634277f, -0.290617011204044f,  0.894427191000000f,
712      0.179611190633958f, -0.940456403667806f,                0.0f,
713      0.179611190634278f, -0.290617011204044f, -0.894427191000000f,
714      0.179611190628666f,  0.760845213037948f, -0.552786404498399f,
715     -0.179611190633958f,  0.940456403667806f,                0.0f,
716     -0.179611190634277f,  0.290617011204044f,  0.894427191000000f,
717     -0.179611190628666f, -0.760845213037948f,  0.552786404498399f,
718     -0.179611190628666f, -0.760845213037948f, -0.552786404498399f,
719     -0.179611190634277f,  0.290617011204044f, -0.894427191000000f,
720     -0.760845213036861f,  0.179611190632978f, -0.552786404500000f,
721     -0.760845213033849f,  0.581234022404097f,                0.0f,
722     -0.760845213036861f,  0.179611190632978f,  0.552786404500000f,
723     -0.760845213037948f, -0.470228201835026f,  0.341640786498800f,
724     -0.760845213037948f, -0.470228201835026f, -0.341640786498800f,
725 };
726
727 /* Vertex indices */
728 static GLubyte icosahedron_vi[ICOSAHEDRON_VERT_PER_OBJ] =
729 {
730     0,   1,  2 ,
731     0,   2,  3 ,
732     0,   3,  4 ,
733     0,   4,  5 ,
734     0,   5,  1 ,
735     1,   8,  2 ,
736     2,   7,  3 ,
737     3,   6,  4 ,
738     4,  10,  5 ,
739     5,   9,  1 ,
740     1,   9,  8 ,
741     2,   8,  7 ,
742     3,   7,  6 ,
743     4,   6, 10 ,
744     5,  10,  9 ,
745     11,  9, 10 ,
746     11,  8,  9 ,
747     11,  7,  8 ,
748     11,  6,  7 ,
749     11, 10,  6 
750 };
751 DECLARE_SHAPE_CACHE(icosahedron,Icosahedron,ICOSAHEDRON)
752
753 /* -- Octahedron -- */
754 #define OCTAHEDRON_NUM_VERT           6
755 #define OCTAHEDRON_NUM_FACES          8
756 #define OCTAHEDRON_NUM_EDGE_PER_FACE  3
757 #define OCTAHEDRON_VERT_PER_OBJ       (OCTAHEDRON_NUM_FACES*OCTAHEDRON_NUM_EDGE_PER_FACE)
758 #define OCTAHEDRON_VERT_ELEM_PER_OBJ  (OCTAHEDRON_VERT_PER_OBJ*3)
759 #define OCTAHEDRON_VERT_PER_OBJ_TRI   OCTAHEDRON_VERT_PER_OBJ
760
761 /* Vertex Coordinates */
762 static GLfloat octahedron_v[OCTAHEDRON_NUM_VERT*3] =
763 {
764      1.f,  0.f,  0.f,
765      0.f,  1.f,  0.f,
766      0.f,  0.f,  1.f,
767     -1.f,  0.f,  0.f,
768      0.f, -1.f,  0.f,
769      0.f,  0.f, -1.f,
770
771 };
772 /* Normal Vectors */
773 static GLfloat octahedron_n[OCTAHEDRON_NUM_FACES*3] =
774 {
775      0.577350269189f, 0.577350269189f, 0.577350269189f,    /* sqrt(1/3) */
776      0.577350269189f, 0.577350269189f,-0.577350269189f,
777      0.577350269189f,-0.577350269189f, 0.577350269189f,
778      0.577350269189f,-0.577350269189f,-0.577350269189f,
779     -0.577350269189f, 0.577350269189f, 0.577350269189f,
780     -0.577350269189f, 0.577350269189f,-0.577350269189f,
781     -0.577350269189f,-0.577350269189f, 0.577350269189f,
782     -0.577350269189f,-0.577350269189f,-0.577350269189f
783
784 };
785
786 /* Vertex indices */
787 static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OBJ] =
788 {
789     0, 1, 2,
790     0, 5, 1,
791     0, 2, 4,
792     0, 4, 5,
793     3, 2, 1,
794     3, 1, 5,
795     3, 4, 2,
796     3, 5, 4
797 };
798 DECLARE_SHAPE_CACHE(octahedron,Octahedron,OCTAHEDRON)
799
800 /* -- RhombicDodecahedron -- */
801 #define RHOMBICDODECAHEDRON_NUM_VERT            14
802 #define RHOMBICDODECAHEDRON_NUM_FACES           12
803 #define RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE   4
804 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ       (RHOMBICDODECAHEDRON_NUM_FACES*RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE)
805 #define RHOMBICDODECAHEDRON_VERT_ELEM_PER_OBJ  (RHOMBICDODECAHEDRON_VERT_PER_OBJ*3)
806 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ_TRI   (RHOMBICDODECAHEDRON_VERT_PER_OBJ+RHOMBICDODECAHEDRON_NUM_FACES*2)    /* 2 extra edges per face when drawing quads as triangles */
807
808 /* Vertex Coordinates */
809 static GLfloat rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] =
810 {
811                 0.0f,             0.0f,  1.0f,
812      0.707106781187f,             0.0f,  0.5f,
813                 0.0f,  0.707106781187f,  0.5f,
814     -0.707106781187f,             0.0f,  0.5f,
815                 0.0f, -0.707106781187f,  0.5f,
816      0.707106781187f,  0.707106781187f,  0.0f,
817     -0.707106781187f,  0.707106781187f,  0.0f,
818     -0.707106781187f, -0.707106781187f,  0.0f,
819      0.707106781187f, -0.707106781187f,  0.0f,
820      0.707106781187f,             0.0f, -0.5f,
821                 0.0f,  0.707106781187f, -0.5f,
822     -0.707106781187f,             0.0f, -0.5f,
823                 0.0f, -0.707106781187f, -0.5f,
824                 0.0f,             0.0f, -1.0f
825 };
826 /* Normal Vectors */
827 static GLfloat rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] =
828 {
829      0.353553390594f,  0.353553390594f,  0.5f,
830     -0.353553390594f,  0.353553390594f,  0.5f,
831     -0.353553390594f, -0.353553390594f,  0.5f,
832      0.353553390594f, -0.353553390594f,  0.5f,
833                 0.0f,             1.0f,  0.0f,
834     -           1.0f,             0.0f,  0.0f,
835                 0.0f, -           1.0f,  0.0f,
836                 1.0f,             0.0f,  0.0f,
837      0.353553390594f,  0.353553390594f, -0.5f,
838     -0.353553390594f,  0.353553390594f, -0.5f,
839     -0.353553390594f, -0.353553390594f, -0.5f,
840      0.353553390594f, -0.353553390594f, -0.5f
841 };
842
843 /* Vertex indices */
844 static GLubyte rhombicdodecahedron_vi[RHOMBICDODECAHEDRON_VERT_PER_OBJ] =
845 {
846     0,  1,  5,  2,
847     0,  2,  6,  3,
848     0,  3,  7,  4,
849     0,  4,  8,  1,
850     5, 10,  6,  2,
851     6, 11,  7,  3,
852     7, 12,  8,  4,
853     8,  9,  5,  1,
854     5,  9, 13, 10,
855     6, 10, 13, 11,
856     7, 11, 13, 12,
857     8, 12, 13,  9
858 };
859 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
860
861 /* -- Tetrahedron -- */
862 /* Magic Numbers:  r0 = ( 1, 0, 0 )
863  *                 r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
864  *                 r2 = ( -1/3, - sqrt(2) / 3,  sqrt(6) / 3 )
865  *                 r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
866  * |r0| = |r1| = |r2| = |r3| = 1
867  * Distance between any two points is 2 sqrt(6) / 3
868  *
869  * Normals:  The unit normals are simply the negative of the coordinates of the point not on the surface.
870  */
871 #define TETRAHEDRON_NUM_VERT            4
872 #define TETRAHEDRON_NUM_FACES           4
873 #define TETRAHEDRON_NUM_EDGE_PER_FACE   3
874 #define TETRAHEDRON_VERT_PER_OBJ        (TETRAHEDRON_NUM_FACES*TETRAHEDRON_NUM_EDGE_PER_FACE)
875 #define TETRAHEDRON_VERT_ELEM_PER_OBJ   (TETRAHEDRON_VERT_PER_OBJ*3)
876 #define TETRAHEDRON_VERT_PER_OBJ_TRI    TETRAHEDRON_VERT_PER_OBJ
877
878 /* Vertex Coordinates */
879 static GLfloat tetrahedron_v[TETRAHEDRON_NUM_VERT*3] =
880 {
881                 1.0f,             0.0f,             0.0f,
882     -0.333333333333f,  0.942809041582f,             0.0f,
883     -0.333333333333f, -0.471404520791f,  0.816496580928f,
884     -0.333333333333f, -0.471404520791f, -0.816496580928f
885 };
886 /* Normal Vectors */
887 static GLfloat tetrahedron_n[TETRAHEDRON_NUM_FACES*3] =
888 {
889     -           1.0f,             0.0f,             0.0f,
890      0.333333333333f, -0.942809041582f,             0.0f,
891      0.333333333333f,  0.471404520791f, -0.816496580928f,
892      0.333333333333f,  0.471404520791f,  0.816496580928f
893 };
894
895 /* Vertex indices */
896 static GLubyte tetrahedron_vi[TETRAHEDRON_VERT_PER_OBJ] =
897 {
898     1, 3, 2,
899     0, 2, 3,
900     0, 3, 1,
901     0, 1, 2
902 };
903 DECLARE_SHAPE_CACHE(tetrahedron,Tetrahedron,TETRAHEDRON)
904
905 /* -- Sierpinski Sponge -- */
906 static unsigned int ipow (int x, unsigned int y)
907 {
908     return y==0? 1: y==1? x: (y%2? x: 1) * ipow(x*x, y/2);
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] = sinf(angle*i);
991         (*cost)[i] = cosf(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 / sqrtf( height * height + base * base ));
1110     const GLfloat sinn = ( (GLfloat)base   / sqrtf( 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 ***/