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