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