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