one more small cleanup
[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     if ((*nVert) > 65535)       /* TODO: must have a better solution than this low limit, at least for architectures where gluint is available */
847         fgWarning("fghGenerateSphere: too many slices or stacks requested, indices will wrap");
848
849     /* precompute values on unit circle */
850     fghCircleTable(&sint1,&cost1,-slices,FALSE);
851     fghCircleTable(&sint2,&cost2, stacks,TRUE);
852
853     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
854     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
855     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
856     if (!(vertices) || !(normals))
857     {
858         free(*vertices);
859         free(*normals);
860         fgError("Failed to allocate memory in fghGenerateSphere");
861     }
862
863     /* top */
864     (*vertices)[0] = 0.f;
865     (*vertices)[1] = 0.f;
866     (*vertices)[2] = radius;
867     (*normals )[0] = 0.f;
868     (*normals )[1] = 0.f;
869     (*normals )[2] = 1.f;
870     idx = 3;
871
872     /* each stack */
873     for( i=1; i<stacks; i++ )
874     {
875         for(j=0; j<slices; j++, idx+=3)
876         {
877             x = cost1[j]*sint2[i];
878             y = sint1[j]*sint2[i];
879             z = cost2[i];
880
881             (*vertices)[idx  ] = x*radius;
882             (*vertices)[idx+1] = y*radius;
883             (*vertices)[idx+2] = z*radius;
884             (*normals )[idx  ] = x;
885             (*normals )[idx+1] = y;
886             (*normals )[idx+2] = z;
887         }
888     }
889
890     /* bottom */
891     (*vertices)[idx  ] =  0.f;
892     (*vertices)[idx+1] =  0.f;
893     (*vertices)[idx+2] = -radius;
894     (*normals )[idx  ] =  0.f;
895     (*normals )[idx+1] =  0.f;
896     (*normals )[idx+2] = -1.f;
897
898     /* Done creating vertices, release sin and cos tables */
899     free(sint1);
900     free(cost1);
901     free(sint2);
902     free(cost2);
903 }
904
905 void fghGenerateCone(
906     GLfloat base, GLfloat height, GLint slices, GLint stacks,   /*  input */
907     GLfloat **vertices, GLfloat **normals, int* nVert           /* output */
908     )
909 {
910     int i,j;
911     int idx = 0;    /* idx into vertex/normal buffer */
912
913     /* Pre-computed circle */
914     GLfloat *sint,*cost;
915
916     /* Step in z and radius as stacks are drawn. */
917     GLfloat z = 0;
918     GLfloat r = (GLfloat)base;
919
920     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
921     const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
922
923     /* Scaling factors for vertex normals */
924 #ifdef __cplusplus
925     const GLfloat cosn = ( (GLfloat)height / sqrtf( height * height + base * base ));
926     const GLfloat sinn = ( (GLfloat)base   / sqrtf( height * height + base * base ));
927 #else
928     const GLfloat cosn = ( (GLfloat)height / (GLfloat)sqrt( (double)(height * height + base * base) ));
929     const GLfloat sinn = ( (GLfloat)base   / (GLfloat)sqrt( (double)(height * height + base * base) ));
930 #endif  /* __cplusplus */
931
932
933
934     /* number of unique vertices */
935     if (slices==0 || stacks<1)
936     {
937         /* nothing to generate */
938         *nVert = 0;
939         return;
940     }
941     *nVert = slices*(stacks+2)+1;   /* need an extra stack for closing off bottom with correct normals */
942
943     if ((*nVert) > 65535)
944         fgWarning("fghGenerateCone: too many slices or stacks requested, indices will wrap");
945
946     /* Pre-computed circle */
947     fghCircleTable(&sint,&cost,-slices,FALSE);
948
949     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
950     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
951     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
952     if (!(vertices) || !(normals))
953     {
954         free(*vertices);
955         free(*normals);
956         fgError("Failed to allocate memory in fghGenerateSphere");
957     }
958
959     /* bottom */
960     (*vertices)[0] =  0.f;
961     (*vertices)[1] =  0.f;
962     (*vertices)[2] =  z;
963     (*normals )[0] =  0.f;
964     (*normals )[1] =  0.f;
965     (*normals )[2] = -1.f;
966     idx = 3;
967     /* other on bottom (get normals right) */
968     for (j=0; j<slices; j++, idx+=3)
969     {
970         (*vertices)[idx  ] = cost[j]*r;
971         (*vertices)[idx+1] = sint[j]*r;
972         (*vertices)[idx+2] = z;
973         (*normals )[idx  ] =  0.f;
974         (*normals )[idx+1] =  0.f;
975         (*normals )[idx+2] = -1.f;
976     }
977
978     /* each stack */
979     for (i=0; i<stacks+1; i++ )
980     {
981         for (j=0; j<slices; j++, idx+=3)
982         {
983             (*vertices)[idx  ] = cost[j]*r;
984             (*vertices)[idx+1] = sint[j]*r;
985             (*vertices)[idx+2] = z;
986             (*normals )[idx  ] = cost[j]*sinn;
987             (*normals )[idx+1] = sint[j]*sinn;
988             (*normals )[idx+2] = cosn;
989         }
990
991         z += zStep;
992         r -= rStep;
993     }
994
995     /* Release sin and cos tables */
996     free(sint);
997     free(cost);
998 }
999
1000 void fghGenerateCylinder(
1001     GLfloat radius, GLfloat height, GLint slices, GLint stacks, /*  input */
1002     GLfloat **vertices, GLfloat **normals, int* nVert           /* output */
1003     )
1004 {
1005     int i,j;
1006     int idx = 0;    /* idx into vertex/normal buffer */
1007
1008     /* Step in z as stacks are drawn. */
1009     GLfloat radf = (GLfloat)radius;
1010     GLfloat z;
1011     const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1012
1013     /* Pre-computed circle */
1014     GLfloat *sint,*cost;
1015
1016     /* number of unique vertices */
1017     if (slices==0 || stacks<1)
1018     {
1019         /* nothing to generate */
1020         *nVert = 0;
1021         return;
1022     }
1023     *nVert = slices*(stacks+3)+2;   /* need two extra stacks for closing off top and bottom with correct normals */
1024
1025     if ((*nVert) > 65535)
1026         fgWarning("fghGenerateCylinder: too many slices or stacks requested, indices will wrap");
1027
1028     /* Pre-computed circle */
1029     fghCircleTable(&sint,&cost,-slices,FALSE);
1030
1031     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1032     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1033     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1034     if (!(vertices) || !(normals))
1035     {
1036         free(*vertices);
1037         free(*normals);
1038         fgError("Failed to allocate memory in fghGenerateCylinder");
1039     }
1040
1041     z=0;
1042     /* top on Z-axis */
1043     (*vertices)[0] =  0.f;
1044     (*vertices)[1] =  0.f;
1045     (*vertices)[2] =  0.f;
1046     (*normals )[0] =  0.f;
1047     (*normals )[1] =  0.f;
1048     (*normals )[2] = -1.f;
1049     idx = 3;
1050     /* other on top (get normals right) */
1051     for (j=0; j<slices; j++, idx+=3)
1052     {
1053         (*vertices)[idx  ] = cost[j]*radf;
1054         (*vertices)[idx+1] = sint[j]*radf;
1055         (*vertices)[idx+2] = z;
1056         (*normals )[idx  ] = 0.f;
1057         (*normals )[idx+1] = 0.f;
1058         (*normals )[idx+2] = -1.f;
1059     }
1060
1061     /* each stack */
1062     for (i=0; i<stacks+1; i++ )
1063     {
1064         for (j=0; j<slices; j++, idx+=3)
1065         {
1066             (*vertices)[idx  ] = cost[j]*radf;
1067             (*vertices)[idx+1] = sint[j]*radf;
1068             (*vertices)[idx+2] = z;
1069             (*normals )[idx  ] = cost[j];
1070             (*normals )[idx+1] = sint[j];
1071             (*normals )[idx+2] = 0.f;
1072         }
1073
1074         z += zStep;
1075     }
1076
1077     /* other on bottom (get normals right) */
1078     z -= zStep;
1079     for (j=0; j<slices; j++, idx+=3)
1080     {
1081         (*vertices)[idx  ] = cost[j]*radf;
1082         (*vertices)[idx+1] = sint[j]*radf;
1083         (*vertices)[idx+2] = z;
1084         (*normals )[idx  ] = 0.f;
1085         (*normals )[idx+1] = 0.f;
1086         (*normals )[idx+2] = 1.f;
1087     }
1088
1089     /* bottom */
1090     (*vertices)[idx  ] =  0.f;
1091     (*vertices)[idx+1] =  0.f;
1092     (*vertices)[idx+2] =  height;
1093     (*normals )[idx  ] =  0.f;
1094     (*normals )[idx+1] =  0.f;
1095     (*normals )[idx+2] =  1.f;
1096
1097     /* Release sin and cos tables */
1098     free(sint);
1099     free(cost);
1100 }
1101 #endif
1102
1103 /* -- INTERNAL DRAWING functions --------------------------------------- */
1104 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
1105     static void fgh##nameICaps( GLboolean useWireMode )\
1106     {\
1107         if (!name##Cached)\
1108         {\
1109             fgh##nameICaps##Generate();\
1110             name##Cached = GL_TRUE;\
1111         }\
1112         \
1113         if (useWireMode)\
1114         {\
1115             fghDrawGeometryWire (name##_verts,name##_norms,\
1116                                                              nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE);\
1117         }\
1118         else\
1119         {\
1120             fghDrawGeometrySolid(name##_verts,name##_norms,vertIdxs,\
1121                                  nameCaps##_VERT_PER_OBJ, nameCaps##_VERT_PER_OBJ_TRI); \
1122         }\
1123     }
1124 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps)                        _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
1125 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
1126
1127 static void fghCube( GLfloat dSize, GLboolean useWireMode )
1128 {
1129     GLfloat *vertices;
1130
1131     if (!cubeCached)
1132     {
1133         fghCubeGenerate();
1134         cubeCached = GL_TRUE;
1135     }
1136
1137     if (dSize!=1.f)
1138     {
1139         /* Need to build new vertex list containing vertices for cube of different size */
1140         int i;
1141
1142         vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
1143
1144         /* Bail out if memory allocation fails, fgError never returns */
1145         if (!vertices)
1146         {
1147             free(vertices);
1148             fgError("Failed to allocate memory in fghCube");
1149         }
1150
1151         for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
1152             vertices[i] = dSize*cube_verts[i];
1153     }
1154     else
1155         vertices = cube_verts;
1156
1157     if (useWireMode)
1158         fghDrawGeometryWire(vertices, cube_norms,
1159                             CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE);
1160     else
1161         fghDrawGeometrySolid(vertices, cube_norms, cube_vertIdxs,
1162                              CUBE_VERT_PER_OBJ, CUBE_VERT_PER_OBJ_TRI);
1163
1164     if (dSize!=1.f)
1165         /* cleanup allocated memory */
1166         free(vertices);
1167 }
1168
1169 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
1170 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON)
1171 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON)
1172 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
1173 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON)
1174
1175 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
1176 {
1177     GLfloat *vertices;
1178     GLfloat * normals;
1179     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
1180     GLsizei    numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
1181     GLsizei    numFace = numTetr*TETRAHEDRON_NUM_FACES;
1182
1183     if (numTetr)
1184     {
1185         /* Allocate memory */
1186         vertices = malloc(numVert*3 * sizeof(GLfloat));
1187         normals  = malloc(numVert*3 * sizeof(GLfloat));
1188         /* Bail out if memory allocation fails, fgError never returns */
1189         if (!vertices || !normals)
1190         {
1191             free(vertices);
1192             free(normals);
1193             fgError("Failed to allocate memory in fghSierpinskiSponge");
1194         }
1195
1196         /* Generate elements */
1197         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
1198
1199         /* Draw and cleanup */
1200         if (useWireMode)
1201             fghDrawGeometryWire (vertices,normals,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE);
1202         else
1203             fghDrawGeometrySolid(vertices,normals,NULL,numVert,numVert);
1204
1205         free(vertices);
1206         free(normals );
1207     }
1208 }
1209
1210
1211 #ifndef GL_ES_VERSION_2_0
1212 static void fghSphere( double radius, GLint slices, GLint stacks, GLboolean useWireMode )
1213 {
1214     int i,j,idx, nVert;
1215     GLfloat *vertices, *normals;
1216
1217     /* Generate vertices and normals */
1218     fghGenerateSphere((GLfloat)radius,slices,stacks,&vertices,&normals,&nVert);
1219     
1220     if (nVert==0)
1221         /* nothing to draw */
1222         return;
1223
1224     if (useWireMode)
1225     {
1226         GLushort  *sliceIdx, *stackIdx;
1227         /* First, generate vertex index arrays for drawing with glDrawElements
1228          * We have a bunch of line_loops to draw for each stack, and a
1229          * bunch for each slice.
1230          */
1231
1232         sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1233         stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort));
1234         if (!(stackIdx) || !(sliceIdx))
1235         {
1236             free(stackIdx);
1237             free(sliceIdx);
1238             fgError("Failed to allocate memory in fghGenerateSphere");
1239         }
1240
1241         /* generate for each stack */
1242         for (i=0,idx=0; i<stacks-1; i++)
1243         {
1244             GLushort offset = 1+i*slices;           /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1245             for (j=0; j<slices; j++, idx++)
1246             {
1247                 stackIdx[idx] = offset+j;
1248             }
1249         }
1250
1251         /* generate for each slice */
1252         for (i=0,idx=0; i<slices; i++)
1253         {
1254             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1255             sliceIdx[idx++] = 0;                    /* vertex on top */
1256             for (j=0; j<stacks-1; j++, idx++)
1257             {
1258                 sliceIdx[idx] = offset+j*slices;
1259             }
1260             sliceIdx[idx++] = nVert-1;              /* zero based index, last element in array... */
1261         }
1262
1263         /* draw */
1264         glEnableClientState(GL_VERTEX_ARRAY);
1265         glEnableClientState(GL_NORMAL_ARRAY);
1266
1267         glVertexPointer(3, GL_FLOAT, 0, vertices);
1268         glNormalPointer(GL_FLOAT, 0, normals);
1269         /*draw slices*/
1270         for (i=0; i<slices; i++)
1271             glDrawElements(GL_LINE_STRIP,stacks+1,GL_UNSIGNED_SHORT,sliceIdx+i*(stacks+1));
1272         /*draw stacks*/
1273         for (i=0; i<stacks-1; i++)
1274             glDrawElements(GL_LINE_LOOP, slices,GL_UNSIGNED_SHORT,stackIdx+i*slices);
1275
1276         glDisableClientState(GL_VERTEX_ARRAY);
1277         glDisableClientState(GL_NORMAL_ARRAY);
1278
1279         /* cleanup allocated memory */
1280         free(sliceIdx);
1281         free(stackIdx);
1282     }
1283     else
1284     {
1285         /* First, generate vertex index arrays for drawing with glDrawElements
1286          * All stacks, including top and bottom are covered with a triangle
1287          * strip.
1288          */
1289         GLushort  *stripIdx;
1290         /* Create index vector */
1291         GLushort offset;
1292
1293         /* Allocate buffers for indices, bail out if memory allocation fails */
1294         stripIdx = malloc((slices+1)*2*(stacks)*sizeof(GLushort));
1295         if (!(stripIdx))
1296         {
1297             free(stripIdx);
1298             fgError("Failed to allocate memory in fghGenerateSphere");
1299         }
1300
1301         /* top stack */
1302         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1303         {
1304             stripIdx[idx  ] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1305             stripIdx[idx+1] = 0;
1306         }
1307         stripIdx[idx  ] = 1;                    /* repeat first slice's idx for closing off shape */
1308         stripIdx[idx+1] = 0;
1309         idx+=2;
1310
1311         /* middle stacks: */
1312         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1313         for (i=0; i<stacks-2; i++, idx+=2)
1314         {
1315             offset = 1+i*slices;                    /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1316             for (j=0; j<slices; j++, idx+=2)
1317             {
1318                 stripIdx[idx  ] = offset+j+slices;
1319                 stripIdx[idx+1] = offset+j;
1320             }
1321             stripIdx[idx  ] = offset+slices;        /* repeat first slice's idx for closing off shape */
1322             stripIdx[idx+1] = offset;
1323         }
1324
1325         /* bottom stack */
1326         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 */
1327         for (j=0; j<slices; j++, idx+=2)
1328         {
1329             stripIdx[idx  ] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1330             stripIdx[idx+1] = offset+j;
1331         }
1332         stripIdx[idx  ] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1333         stripIdx[idx+1] = offset;
1334
1335
1336         /* draw */
1337         glEnableClientState(GL_VERTEX_ARRAY);
1338         glEnableClientState(GL_NORMAL_ARRAY);
1339
1340         glVertexPointer(3, GL_FLOAT, 0, vertices);
1341         glNormalPointer(GL_FLOAT, 0, normals);
1342         /*draw stacks*/
1343         for (i=0; i<stacks; i++)
1344             glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1345
1346         glDisableClientState(GL_VERTEX_ARRAY);
1347         glDisableClientState(GL_NORMAL_ARRAY);
1348
1349         /* cleanup allocated memory */
1350         free(stripIdx);
1351     }
1352     
1353     /* cleanup allocated memory */
1354     free(vertices);
1355     free(normals);
1356 }
1357
1358 static void fghCone( double base, double height, GLint slices, GLint stacks, GLboolean useWireMode )
1359 {
1360     int i,j,idx, nVert;
1361     GLfloat *vertices, *normals;
1362
1363     /* Generate vertices and normals */
1364     /* Note, (stacks+1)*slices vertices for side of object, slices+1 for top and bottom closures */
1365     fghGenerateCone((GLfloat)base,(GLfloat)height,slices,stacks,&vertices,&normals,&nVert);
1366
1367     if (nVert==0)
1368         /* nothing to draw */
1369         return;
1370
1371     if (useWireMode)
1372     {
1373         GLushort  *sliceIdx, *stackIdx;
1374         /* First, generate vertex index arrays for drawing with glDrawElements
1375          * We have a bunch of line_loops to draw for each stack, and a
1376          * bunch for each slice.
1377          */
1378
1379         stackIdx = malloc(slices*stacks*sizeof(GLushort));
1380         sliceIdx = malloc(slices*2     *sizeof(GLushort));
1381         if (!(stackIdx) || !(sliceIdx))
1382         {
1383             free(stackIdx);
1384             free(sliceIdx);
1385             fgError("Failed to allocate memory in fghGenerateCone");
1386         }
1387
1388         /* generate for each stack */
1389         for (i=0,idx=0; i<stacks; i++)
1390         {
1391             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1392             for (j=0; j<slices; j++, idx++)
1393             {
1394                 stackIdx[idx] = offset+j;
1395             }
1396         }
1397
1398         /* generate for each slice */
1399         for (i=0,idx=0; i<slices; i++)
1400         {
1401             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1402             sliceIdx[idx++] = offset+slices;
1403             sliceIdx[idx++] = offset+(stacks+1)*slices;
1404         }
1405
1406         /* draw */
1407         glEnableClientState(GL_VERTEX_ARRAY);
1408         glEnableClientState(GL_NORMAL_ARRAY);
1409
1410         glVertexPointer(3, GL_FLOAT, 0, vertices);
1411         glNormalPointer(GL_FLOAT, 0, normals);
1412         /*draw slices*/
1413         glDrawElements(GL_LINES,slices*2,GL_UNSIGNED_SHORT,sliceIdx);
1414         /*draw stacks*/
1415         for (i=0; i<stacks; i++)
1416             glDrawElements(GL_LINE_LOOP, slices,GL_UNSIGNED_SHORT,stackIdx+i*slices);
1417
1418         glDisableClientState(GL_VERTEX_ARRAY);
1419         glDisableClientState(GL_NORMAL_ARRAY);
1420
1421         /* cleanup allocated memory */
1422         free(sliceIdx);
1423         free(stackIdx);
1424     }
1425     else
1426     {
1427         /* First, generate vertex index arrays for drawing with glDrawElements
1428          * All stacks, including top and bottom are covered with a triangle
1429          * strip.
1430          */
1431         GLushort  *stripIdx;
1432         /* Create index vector */
1433         GLushort offset;
1434
1435         /* Allocate buffers for indices, bail out if memory allocation fails */
1436         stripIdx = malloc((slices+1)*2*(stacks+1)*sizeof(GLushort));    /*stacks +1 because of closing off bottom */
1437         if (!(stripIdx))
1438         {
1439             free(stripIdx);
1440             fgError("Failed to allocate memory in fghGenerateCone");
1441         }
1442
1443         /* top stack */
1444         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1445         {
1446             stripIdx[idx  ] = 0;
1447             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1448         }
1449         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1450         stripIdx[idx+1] = 1;
1451         idx+=2;
1452
1453         /* middle stacks: */
1454         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1455         for (i=0; i<stacks; i++, idx+=2)
1456         {
1457             offset = 1+(i+1)*slices;                /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1458             for (j=0; j<slices; j++, idx+=2)
1459             {
1460                 stripIdx[idx  ] = offset+j;
1461                 stripIdx[idx+1] = offset+j+slices;
1462             }
1463             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1464             stripIdx[idx+1] = offset+slices;
1465         }
1466
1467         /* draw */
1468         glEnableClientState(GL_VERTEX_ARRAY);
1469         glEnableClientState(GL_NORMAL_ARRAY);
1470
1471         glVertexPointer(3, GL_FLOAT, 0, vertices);
1472         glNormalPointer(GL_FLOAT, 0, normals);
1473         /*draw stacks*/
1474         for (i=0; i<stacks+1; i++)
1475             glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1476
1477         glDisableClientState(GL_VERTEX_ARRAY);
1478         glDisableClientState(GL_NORMAL_ARRAY);
1479
1480         /* cleanup allocated memory */
1481         free(stripIdx);
1482     }
1483
1484     /* cleanup allocated memory */
1485     free(vertices);
1486     free(normals);
1487 }
1488
1489 static void fghCylinder( double radius, double height, GLint slices, GLint stacks, GLboolean useWireMode )
1490 {
1491     int i,j,idx, nVert;
1492     GLfloat *vertices, *normals;
1493
1494     /* Generate vertices and normals */
1495     /* Note, (stacks+1)*slices vertices for side of object, 2*slices+2 for top and bottom closures */
1496     fghGenerateCylinder((GLfloat)radius,(GLfloat)height,slices,stacks,&vertices,&normals,&nVert);
1497
1498     if (nVert==0)
1499         /* nothing to draw */
1500         return;
1501
1502     if (useWireMode)
1503     {
1504         GLushort  *sliceIdx, *stackIdx;
1505         /* First, generate vertex index arrays for drawing with glDrawElements
1506          * We have a bunch of line_loops to draw for each stack, and a
1507          * bunch for each slice.
1508          */
1509
1510         stackIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1511         sliceIdx = malloc(slices*2         *sizeof(GLushort));
1512         if (!(stackIdx) || !(sliceIdx))
1513         {
1514             free(stackIdx);
1515             free(sliceIdx);
1516             fgError("Failed to allocate memory in fghGenerateCylinder");
1517         }
1518
1519         /* generate for each stack */
1520         for (i=0,idx=0; i<stacks+1; i++)
1521         {
1522             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1523             for (j=0; j<slices; j++, idx++)
1524             {
1525                 stackIdx[idx] = offset+j;
1526             }
1527         }
1528
1529         /* generate for each slice */
1530         for (i=0,idx=0; i<slices; i++)
1531         {
1532             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1533             sliceIdx[idx++] = offset+slices;
1534             sliceIdx[idx++] = offset+(stacks+1)*slices;
1535         }
1536
1537         /* draw */
1538         glEnableClientState(GL_VERTEX_ARRAY);
1539         glEnableClientState(GL_NORMAL_ARRAY);
1540
1541         glVertexPointer(3, GL_FLOAT, 0, vertices);
1542         glNormalPointer(GL_FLOAT, 0, normals);
1543         /*draw slices*/
1544         glDrawElements(GL_LINES,slices*2,GL_UNSIGNED_SHORT,sliceIdx);
1545         /*draw stacks*/
1546         for (i=0; i<stacks+1; i++)
1547             glDrawElements(GL_LINE_LOOP, slices,GL_UNSIGNED_SHORT,stackIdx+i*slices);
1548
1549         glDisableClientState(GL_VERTEX_ARRAY);
1550         glDisableClientState(GL_NORMAL_ARRAY);
1551
1552         /* cleanup allocated memory */
1553         free(sliceIdx);
1554         free(stackIdx);
1555     }
1556     else
1557     {
1558         /* First, generate vertex index arrays for drawing with glDrawElements
1559          * All stacks, including top and bottom are covered with a triangle
1560          * strip.
1561          */
1562         GLushort  *stripIdx;
1563         /* Create index vector */
1564         GLushort offset;
1565
1566         /* Allocate buffers for indices, bail out if memory allocation fails */
1567         stripIdx = malloc((slices+1)*2*(stacks+2)*sizeof(GLushort));    /*stacks +2 because of closing off bottom and top */
1568         if (!(stripIdx))
1569         {
1570             free(stripIdx);
1571             fgError("Failed to allocate memory in fghGenerateCylinder");
1572         }
1573
1574         /* top stack */
1575         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1576         {
1577             stripIdx[idx  ] = 0;
1578             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1579         }
1580         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1581         stripIdx[idx+1] = 1;
1582         idx+=2;
1583
1584         /* middle stacks: */
1585         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1586         for (i=0; i<stacks; i++, idx+=2)
1587         {
1588             offset = 1+(i+1)*slices;                /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1589             for (j=0; j<slices; j++, idx+=2)
1590             {
1591                 stripIdx[idx  ] = offset+j;
1592                 stripIdx[idx+1] = offset+j+slices;
1593             }
1594             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1595             stripIdx[idx+1] = offset+slices;
1596         }
1597
1598         /* top stack */
1599         offset = 1+(stacks+2)*slices;
1600         for (j=0; j<slices; j++, idx+=2)
1601         {
1602             stripIdx[idx  ] = offset+j;
1603             stripIdx[idx+1] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1604         }
1605         stripIdx[idx  ] = offset;
1606         stripIdx[idx+1] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1607
1608         /* draw */
1609         glEnableClientState(GL_VERTEX_ARRAY);
1610         glEnableClientState(GL_NORMAL_ARRAY);
1611
1612         glVertexPointer(3, GL_FLOAT, 0, vertices);
1613         glNormalPointer(GL_FLOAT, 0, normals);
1614         /*draw stacks*/
1615         for (i=0; i<stacks+2; i++)
1616             glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1617
1618         glDisableClientState(GL_VERTEX_ARRAY);
1619         glDisableClientState(GL_NORMAL_ARRAY);
1620
1621         /* cleanup allocated memory */
1622         free(stripIdx);
1623     }
1624
1625     /* cleanup allocated memory */
1626     free(vertices);
1627     free(normals);
1628 }
1629
1630
1631 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
1632
1633
1634 /*
1635  * Draws a solid sphere
1636  */
1637 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
1638 {
1639     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
1640
1641     fghSphere( radius, slices, stacks, FALSE );
1642 }
1643
1644 /*
1645  * Draws a wire sphere
1646  */
1647 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
1648 {
1649     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
1650
1651     fghSphere( radius, slices, stacks, TRUE );
1652     
1653 }
1654 #endif /* GL_ES_VERSION_2_0 */
1655
1656 #ifndef EGL_VERSION_1_0
1657 /*
1658  * Draws a solid cone
1659  */
1660 void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks )
1661 {
1662     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
1663
1664     fghCone( base, height, slices, stacks, FALSE );
1665 }
1666
1667 /*
1668  * Draws a wire cone
1669  */
1670 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
1671 {
1672     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1673
1674     fghCone( base, height, slices, stacks, TRUE );
1675 }
1676
1677
1678 /*
1679  * Draws a solid cylinder
1680  */
1681 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
1682 {
1683     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1684
1685     fghCylinder( radius, height, slices, stacks, FALSE );
1686 }
1687
1688 /*
1689  * Draws a wire cylinder
1690  */
1691 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
1692 {
1693     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1694
1695     fghCylinder( radius, height, slices, stacks, TRUE );
1696 }
1697
1698 /*
1699  * Draws a wire torus
1700  */
1701 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1702 {
1703   GLfloat  iradius = (float)dInnerRadius, oradius = (float)dOuterRadius;
1704   GLfloat phi, psi, dpsi, dphi;
1705   GLfloat *vertex, *normal;
1706   int    i, j;
1707   GLfloat spsi, cpsi, sphi, cphi ;
1708
1709   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1710
1711   if ( nSides < 1 ) nSides = 1;
1712   if ( nRings < 1 ) nRings = 1;
1713
1714   /* Allocate the vertices array */
1715   vertex = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1716   normal = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1717
1718   glPushMatrix();
1719
1720   dpsi =  2.0f * (GLfloat)M_PI / (GLfloat)(nRings) ;
1721   dphi = -2.0f * (GLfloat)M_PI / (GLfloat)(nSides) ;
1722   psi  = 0.0f;
1723
1724   for( j=0; j<nRings; j++ )
1725   {
1726 #ifdef __cplusplus
1727     cpsi = cosf( psi ) ;
1728     spsi = sinf( psi ) ;
1729 #else
1730     cpsi = (float)cos( (double)psi ) ;
1731     spsi = (float)sin( (double)psi ) ;
1732 #endif  /* __cplusplus */
1733     phi = 0.0f;
1734
1735     for( i=0; i<nSides; i++ )
1736     {
1737       int offset = 3 * ( j * nSides + i ) ;
1738 #ifdef __cplusplus
1739       cphi = cosf( phi ) ;
1740       sphi = sinf( phi ) ;
1741 #else
1742       cphi = (float)cos( (double)phi ) ;
1743       sphi = (float)sin( (double)phi ) ;
1744 #endif  /* __cplusplus */
1745       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1746       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1747       *(vertex + offset + 2) =                    sphi * iradius  ;
1748       *(normal + offset + 0) = cpsi * cphi ;
1749       *(normal + offset + 1) = spsi * cphi ;
1750       *(normal + offset + 2) =        sphi ;
1751       phi += dphi;
1752     }
1753
1754     psi += dpsi;
1755   }
1756
1757   for( i=0; i<nSides; i++ )
1758   {
1759     glBegin( GL_LINE_LOOP );
1760
1761     for( j=0; j<nRings; j++ )
1762     {
1763       int offset = 3 * ( j * nSides + i ) ;
1764       glNormal3fv( normal + offset );
1765       glVertex3fv( vertex + offset );
1766     }
1767
1768     glEnd();
1769   }
1770
1771   for( j=0; j<nRings; j++ )
1772   {
1773     glBegin(GL_LINE_LOOP);
1774
1775     for( i=0; i<nSides; i++ )
1776     {
1777       int offset = 3 * ( j * nSides + i ) ;
1778       glNormal3fv( normal + offset );
1779       glVertex3fv( vertex + offset );
1780     }
1781
1782     glEnd();
1783   }
1784
1785   free ( vertex ) ;
1786   free ( normal ) ;
1787   glPopMatrix();
1788 }
1789
1790 /*
1791  * Draws a solid torus
1792  */
1793 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1794 {
1795   GLfloat  iradius = (float)dInnerRadius, oradius = (float)dOuterRadius;
1796   GLfloat phi, psi, dpsi, dphi;
1797   GLfloat *vertex, *normal;
1798   int    i, j;
1799   GLfloat spsi, cpsi, sphi, cphi ;
1800
1801   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1802
1803   if ( nSides < 1 ) nSides = 1;
1804   if ( nRings < 1 ) nRings = 1;
1805
1806   /* Increment the number of sides and rings to allow for one more point than surface */
1807   nSides ++ ;
1808   nRings ++ ;
1809
1810   /* Allocate the vertices array */
1811   vertex = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1812   normal = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1813
1814   glPushMatrix();
1815
1816   dpsi =  2.0f * (GLfloat)M_PI / (GLfloat)(nRings - 1) ;
1817   dphi = -2.0f * (GLfloat)M_PI / (GLfloat)(nSides - 1) ;
1818   psi  = 0.0f;
1819
1820   for( j=0; j<nRings; j++ )
1821   {
1822 #ifdef __cplusplus
1823     cpsi = cosf( psi ) ;
1824     spsi = sinf( psi ) ;
1825 #else
1826     cpsi = (float)cos( (double)psi ) ;
1827     spsi = (float)sin( (double)psi ) ;
1828 #endif  /* __cplusplus */
1829     phi = 0.0f;
1830
1831     for( i=0; i<nSides; i++ )
1832     {
1833       int offset = 3 * ( j * nSides + i ) ;
1834 #ifdef __cplusplus
1835       cphi = cosf( phi ) ;
1836       sphi = sinf( phi ) ;
1837 #else
1838       cphi = (float)cos( (double)phi ) ;
1839       sphi = (float)sin( (double)phi ) ;
1840 #endif  /* __cplusplus */
1841       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1842       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1843       *(vertex + offset + 2) =                    sphi * iradius  ;
1844       *(normal + offset + 0) = cpsi * cphi ;
1845       *(normal + offset + 1) = spsi * cphi ;
1846       *(normal + offset + 2) =        sphi ;
1847       phi += dphi;
1848     }
1849
1850     psi += dpsi;
1851   }
1852
1853     glBegin( GL_QUADS );
1854   for( i=0; i<nSides-1; i++ )
1855   {
1856     for( j=0; j<nRings-1; j++ )
1857     {
1858       int offset = 3 * ( j * nSides + i ) ;
1859       glNormal3fv( normal + offset );
1860       glVertex3fv( vertex + offset );
1861       glNormal3fv( normal + offset + 3 );
1862       glVertex3fv( vertex + offset + 3 );
1863       glNormal3fv( normal + offset + 3 * nSides + 3 );
1864       glVertex3fv( vertex + offset + 3 * nSides + 3 );
1865       glNormal3fv( normal + offset + 3 * nSides );
1866       glVertex3fv( vertex + offset + 3 * nSides );
1867     }
1868   }
1869
1870   glEnd();
1871
1872   free ( vertex ) ;
1873   free ( normal ) ;
1874   glPopMatrix();
1875 }
1876 #endif /* EGL_VERSION_1_0 */
1877
1878
1879
1880 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1881 /* Macro to generate interface functions */
1882 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1883     void FGAPIENTRY glutWire##nameICaps( void )\
1884     {\
1885         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1886         fgh##nameICaps( TRUE );\
1887     }\
1888     void FGAPIENTRY glutSolid##nameICaps( void )\
1889     {\
1890         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1891         fgh##nameICaps( FALSE );\
1892     }
1893
1894 void FGAPIENTRY glutWireCube( double dSize )
1895 {
1896     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1897     fghCube( (GLfloat)dSize, TRUE );
1898 }
1899 void FGAPIENTRY glutSolidCube( double dSize )
1900 {
1901     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1902     fghCube( (GLfloat)dSize, FALSE );
1903 }
1904
1905 DECLARE_SHAPE_INTERFACE(Dodecahedron)
1906 DECLARE_SHAPE_INTERFACE(Icosahedron)
1907 DECLARE_SHAPE_INTERFACE(Octahedron)
1908 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron)
1909
1910 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
1911 {
1912     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1913     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, TRUE );
1914 }
1915 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
1916 {
1917     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1918     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, FALSE );
1919 }
1920
1921 DECLARE_SHAPE_INTERFACE(Tetrahedron)
1922
1923
1924 /*** END OF FILE ***/