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