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