less repetition of vertices for triangle strip
[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
1102 void fghGenerateTorus(
1103     double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings, /*  input */
1104     GLfloat **vertices, GLfloat **normals, int* nVert                     /* output */
1105     )
1106 {
1107     GLfloat  iradius = (float)dInnerRadius;
1108     GLfloat  oradius = (float)dOuterRadius;
1109     int    i, j;
1110
1111     /* Pre-computed circle */
1112     GLfloat *spsi, *cpsi;
1113     GLfloat *sphi, *cphi;
1114
1115     /* number of unique vertices */
1116     if (nSides<2 || nRings<2)
1117     {
1118         /* nothing to generate */
1119         *nVert = 0;
1120         return;
1121     }
1122     *nVert = nSides * nRings;
1123
1124     if ((*nVert) > 65535)
1125         fgWarning("fghGenerateTorus: too many slices or stacks requested, indices will wrap");
1126
1127     /* precompute values on unit circle */
1128     fghCircleTable(&spsi,&cpsi, nRings,FALSE);
1129     fghCircleTable(&sphi,&cphi,-nSides,FALSE);
1130
1131     /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1132     *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1133     *normals  = malloc((*nVert)*3*sizeof(GLfloat));
1134     if (!(*vertices) || !(*normals))
1135     {
1136         free(*vertices);
1137         free(*normals);
1138         fgError("Failed to allocate memory in fghGenerateTorus");
1139     }
1140
1141     for( j=0; j<nRings; j++ )
1142     {
1143         for( i=0; i<nSides; i++ )
1144         {
1145             int offset = 3 * ( j * nSides + i ) ;
1146
1147             (*vertices)[offset  ] = cpsi[j] * ( oradius + cphi[i] * iradius ) ;
1148             (*vertices)[offset+1] = spsi[j] * ( oradius + cphi[i] * iradius ) ;
1149             (*vertices)[offset+2] =                       sphi[i] * iradius  ;
1150             (*normals )[offset  ] = cpsi[j] * cphi[i] ;
1151             (*normals )[offset+1] = spsi[j] * cphi[i] ;
1152             (*normals )[offset+2] =           sphi[i] ;
1153         }
1154     }
1155
1156     /* Release sin and cos tables */
1157     free(spsi);
1158     free(cpsi);
1159     free(sphi);
1160     free(cphi);
1161 }
1162 #endif
1163
1164 /* -- INTERNAL DRAWING functions --------------------------------------- */
1165 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
1166     static void fgh##nameICaps( GLboolean useWireMode )\
1167     {\
1168         if (!name##Cached)\
1169         {\
1170             fgh##nameICaps##Generate();\
1171             name##Cached = GL_TRUE;\
1172         }\
1173         \
1174         if (useWireMode)\
1175         {\
1176             fghDrawGeometryWire (name##_verts,name##_norms,\
1177                                                              nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE);\
1178         }\
1179         else\
1180         {\
1181             fghDrawGeometrySolid(name##_verts,name##_norms,vertIdxs,\
1182                                  nameCaps##_VERT_PER_OBJ, nameCaps##_VERT_PER_OBJ_TRI); \
1183         }\
1184     }
1185 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps)                        _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
1186 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
1187
1188 static void fghCube( GLfloat dSize, GLboolean useWireMode )
1189 {
1190     GLfloat *vertices;
1191
1192     if (!cubeCached)
1193     {
1194         fghCubeGenerate();
1195         cubeCached = GL_TRUE;
1196     }
1197
1198     if (dSize!=1.f)
1199     {
1200         /* Need to build new vertex list containing vertices for cube of different size */
1201         int i;
1202
1203         vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
1204
1205         /* Bail out if memory allocation fails, fgError never returns */
1206         if (!vertices)
1207         {
1208             free(vertices);
1209             fgError("Failed to allocate memory in fghCube");
1210         }
1211
1212         for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
1213             vertices[i] = dSize*cube_verts[i];
1214     }
1215     else
1216         vertices = cube_verts;
1217
1218     if (useWireMode)
1219         fghDrawGeometryWire(vertices, cube_norms,
1220                             CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE);
1221     else
1222         fghDrawGeometrySolid(vertices, cube_norms, cube_vertIdxs,
1223                              CUBE_VERT_PER_OBJ, CUBE_VERT_PER_OBJ_TRI);
1224
1225     if (dSize!=1.f)
1226         /* cleanup allocated memory */
1227         free(vertices);
1228 }
1229
1230 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
1231 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON)
1232 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON)
1233 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
1234 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON)
1235
1236 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
1237 {
1238     GLfloat *vertices;
1239     GLfloat * normals;
1240     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
1241     GLsizei    numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
1242     GLsizei    numFace = numTetr*TETRAHEDRON_NUM_FACES;
1243
1244     if (numTetr)
1245     {
1246         /* Allocate memory */
1247         vertices = malloc(numVert*3 * sizeof(GLfloat));
1248         normals  = malloc(numVert*3 * sizeof(GLfloat));
1249         /* Bail out if memory allocation fails, fgError never returns */
1250         if (!vertices || !normals)
1251         {
1252             free(vertices);
1253             free(normals);
1254             fgError("Failed to allocate memory in fghSierpinskiSponge");
1255         }
1256
1257         /* Generate elements */
1258         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
1259
1260         /* Draw and cleanup */
1261         if (useWireMode)
1262             fghDrawGeometryWire (vertices,normals,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE);
1263         else
1264             fghDrawGeometrySolid(vertices,normals,NULL,numVert,numVert);
1265
1266         free(vertices);
1267         free(normals );
1268     }
1269 }
1270
1271
1272 #ifndef GL_ES_VERSION_2_0
1273 static void fghSphere( double radius, GLint slices, GLint stacks, GLboolean useWireMode )
1274 {
1275     int i,j,idx, nVert;
1276     GLfloat *vertices, *normals;
1277
1278     /* Generate vertices and normals */
1279     fghGenerateSphere((GLfloat)radius,slices,stacks,&vertices,&normals,&nVert);
1280     
1281     if (nVert==0)
1282         /* nothing to draw */
1283         return;
1284
1285     if (useWireMode)
1286     {
1287         GLushort  *sliceIdx, *stackIdx;
1288         /* First, generate vertex index arrays for drawing with glDrawElements
1289          * We have a bunch of line_loops to draw for each stack, and a
1290          * bunch for each slice.
1291          */
1292
1293         sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1294         stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort));
1295         if (!(stackIdx) || !(sliceIdx))
1296         {
1297             free(stackIdx);
1298             free(sliceIdx);
1299             fgError("Failed to allocate memory in fghSphere");
1300         }
1301
1302         /* generate for each stack */
1303         for (i=0,idx=0; i<stacks-1; i++)
1304         {
1305             GLushort offset = 1+i*slices;           /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1306             for (j=0; j<slices; j++, idx++)
1307             {
1308                 stackIdx[idx] = offset+j;
1309             }
1310         }
1311
1312         /* generate for each slice */
1313         for (i=0,idx=0; i<slices; i++)
1314         {
1315             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1316             sliceIdx[idx++] = 0;                    /* vertex on top */
1317             for (j=0; j<stacks-1; j++, idx++)
1318             {
1319                 sliceIdx[idx] = offset+j*slices;
1320             }
1321             sliceIdx[idx++] = nVert-1;              /* zero based index, last element in array... */
1322         }
1323
1324         /* draw */
1325         glEnableClientState(GL_VERTEX_ARRAY);
1326         glEnableClientState(GL_NORMAL_ARRAY);
1327
1328         glVertexPointer(3, GL_FLOAT, 0, vertices);
1329         glNormalPointer(GL_FLOAT, 0, normals);
1330         /*draw slices*/
1331         for (i=0; i<slices; i++)
1332             glDrawElements(GL_LINE_STRIP,stacks+1,GL_UNSIGNED_SHORT,sliceIdx+i*(stacks+1));
1333         /*draw stacks*/
1334         for (i=0; i<stacks-1; i++)
1335             glDrawElements(GL_LINE_LOOP, slices,GL_UNSIGNED_SHORT,stackIdx+i*slices);
1336
1337         glDisableClientState(GL_VERTEX_ARRAY);
1338         glDisableClientState(GL_NORMAL_ARRAY);
1339
1340         /* cleanup allocated memory */
1341         free(sliceIdx);
1342         free(stackIdx);
1343     }
1344     else
1345     {
1346         /* First, generate vertex index arrays for drawing with glDrawElements
1347          * All stacks, including top and bottom are covered with a triangle
1348          * strip.
1349          */
1350         GLushort  *stripIdx;
1351         /* Create index vector */
1352         GLushort offset;
1353
1354         /* Allocate buffers for indices, bail out if memory allocation fails */
1355         stripIdx = malloc((slices+1)*2*(stacks)*sizeof(GLushort));
1356         if (!(stripIdx))
1357         {
1358             free(stripIdx);
1359             fgError("Failed to allocate memory in fghSphere");
1360         }
1361
1362         /* top stack */
1363         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1364         {
1365             stripIdx[idx  ] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1366             stripIdx[idx+1] = 0;
1367         }
1368         stripIdx[idx  ] = 1;                    /* repeat first slice's idx for closing off shape */
1369         stripIdx[idx+1] = 0;
1370         idx+=2;
1371
1372         /* middle stacks: */
1373         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1374         for (i=0; i<stacks-2; i++, idx+=2)
1375         {
1376             offset = 1+i*slices;                    /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1377             for (j=0; j<slices; j++, idx+=2)
1378             {
1379                 stripIdx[idx  ] = offset+j+slices;
1380                 stripIdx[idx+1] = offset+j;
1381             }
1382             stripIdx[idx  ] = offset+slices;        /* repeat first slice's idx for closing off shape */
1383             stripIdx[idx+1] = offset;
1384         }
1385
1386         /* bottom stack */
1387         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 */
1388         for (j=0; j<slices; j++, idx+=2)
1389         {
1390             stripIdx[idx  ] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1391             stripIdx[idx+1] = offset+j;
1392         }
1393         stripIdx[idx  ] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1394         stripIdx[idx+1] = offset;
1395
1396
1397         /* draw */
1398         glEnableClientState(GL_VERTEX_ARRAY);
1399         glEnableClientState(GL_NORMAL_ARRAY);
1400
1401         glVertexPointer(3, GL_FLOAT, 0, vertices);
1402         glNormalPointer(GL_FLOAT, 0, normals);
1403         /*draw stacks*/
1404         for (i=0; i<stacks; i++)
1405             glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1406
1407         glDisableClientState(GL_VERTEX_ARRAY);
1408         glDisableClientState(GL_NORMAL_ARRAY);
1409
1410         /* cleanup allocated memory */
1411         free(stripIdx);
1412     }
1413     
1414     /* cleanup allocated memory */
1415     free(vertices);
1416     free(normals);
1417 }
1418
1419 static void fghCone( double base, double height, GLint slices, GLint stacks, GLboolean useWireMode )
1420 {
1421     int i,j,idx, nVert;
1422     GLfloat *vertices, *normals;
1423
1424     /* Generate vertices and normals */
1425     /* Note, (stacks+1)*slices vertices for side of object, slices+1 for top and bottom closures */
1426     fghGenerateCone((GLfloat)base,(GLfloat)height,slices,stacks,&vertices,&normals,&nVert);
1427
1428     if (nVert==0)
1429         /* nothing to draw */
1430         return;
1431
1432     if (useWireMode)
1433     {
1434         GLushort  *sliceIdx, *stackIdx;
1435         /* First, generate vertex index arrays for drawing with glDrawElements
1436          * We have a bunch of line_loops to draw for each stack, and a
1437          * bunch for each slice.
1438          */
1439
1440         stackIdx = malloc(slices*stacks*sizeof(GLushort));
1441         sliceIdx = malloc(slices*2     *sizeof(GLushort));
1442         if (!(stackIdx) || !(sliceIdx))
1443         {
1444             free(stackIdx);
1445             free(sliceIdx);
1446             fgError("Failed to allocate memory in fghCone");
1447         }
1448
1449         /* generate for each stack */
1450         for (i=0,idx=0; i<stacks; i++)
1451         {
1452             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1453             for (j=0; j<slices; j++, idx++)
1454             {
1455                 stackIdx[idx] = offset+j;
1456             }
1457         }
1458
1459         /* generate for each slice */
1460         for (i=0,idx=0; i<slices; i++)
1461         {
1462             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1463             sliceIdx[idx++] = offset+slices;
1464             sliceIdx[idx++] = offset+(stacks+1)*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 slices*/
1474         glDrawElements(GL_LINES,slices*2,GL_UNSIGNED_SHORT,sliceIdx);
1475         /*draw stacks*/
1476         for (i=0; i<stacks; i++)
1477             glDrawElements(GL_LINE_LOOP, slices,GL_UNSIGNED_SHORT,stackIdx+i*slices);
1478
1479         glDisableClientState(GL_VERTEX_ARRAY);
1480         glDisableClientState(GL_NORMAL_ARRAY);
1481
1482         /* cleanup allocated memory */
1483         free(sliceIdx);
1484         free(stackIdx);
1485     }
1486     else
1487     {
1488         /* First, generate vertex index arrays for drawing with glDrawElements
1489          * All stacks, including top and bottom are covered with a triangle
1490          * strip.
1491          */
1492         GLushort  *stripIdx;
1493         /* Create index vector */
1494         GLushort offset;
1495
1496         /* Allocate buffers for indices, bail out if memory allocation fails */
1497         stripIdx = malloc((slices+1)*2*(stacks+1)*sizeof(GLushort));    /*stacks +1 because of closing off bottom */
1498         if (!(stripIdx))
1499         {
1500             free(stripIdx);
1501             fgError("Failed to allocate memory in fghCone");
1502         }
1503
1504         /* top stack */
1505         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1506         {
1507             stripIdx[idx  ] = 0;
1508             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1509         }
1510         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1511         stripIdx[idx+1] = 1;
1512         idx+=2;
1513
1514         /* middle stacks: */
1515         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1516         for (i=0; i<stacks; i++, idx+=2)
1517         {
1518             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 */
1519             for (j=0; j<slices; j++, idx+=2)
1520             {
1521                 stripIdx[idx  ] = offset+j;
1522                 stripIdx[idx+1] = offset+j+slices;
1523             }
1524             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1525             stripIdx[idx+1] = offset+slices;
1526         }
1527
1528         /* draw */
1529         glEnableClientState(GL_VERTEX_ARRAY);
1530         glEnableClientState(GL_NORMAL_ARRAY);
1531
1532         glVertexPointer(3, GL_FLOAT, 0, vertices);
1533         glNormalPointer(GL_FLOAT, 0, normals);
1534         /*draw stacks*/
1535         for (i=0; i<stacks+1; i++)
1536             glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1537
1538         glDisableClientState(GL_VERTEX_ARRAY);
1539         glDisableClientState(GL_NORMAL_ARRAY);
1540
1541         /* cleanup allocated memory */
1542         free(stripIdx);
1543     }
1544
1545     /* cleanup allocated memory */
1546     free(vertices);
1547     free(normals);
1548 }
1549
1550 static void fghCylinder( double radius, double height, GLint slices, GLint stacks, GLboolean useWireMode )
1551 {
1552     int i,j,idx, nVert;
1553     GLfloat *vertices, *normals;
1554
1555     /* Generate vertices and normals */
1556     /* Note, (stacks+1)*slices vertices for side of object, 2*slices+2 for top and bottom closures */
1557     fghGenerateCylinder((GLfloat)radius,(GLfloat)height,slices,stacks,&vertices,&normals,&nVert);
1558
1559     if (nVert==0)
1560         /* nothing to draw */
1561         return;
1562
1563     if (useWireMode)
1564     {
1565         GLushort  *sliceIdx, *stackIdx;
1566         /* First, generate vertex index arrays for drawing with glDrawElements
1567          * We have a bunch of line_loops to draw for each stack, and a
1568          * bunch for each slice.
1569          */
1570
1571         stackIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1572         sliceIdx = malloc(slices*2         *sizeof(GLushort));
1573         if (!(stackIdx) || !(sliceIdx))
1574         {
1575             free(stackIdx);
1576             free(sliceIdx);
1577             fgError("Failed to allocate memory in fghCylinder");
1578         }
1579
1580         /* generate for each stack */
1581         for (i=0,idx=0; i<stacks+1; i++)
1582         {
1583             GLushort offset = 1+(i+1)*slices;       /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1584             for (j=0; j<slices; j++, idx++)
1585             {
1586                 stackIdx[idx] = offset+j;
1587             }
1588         }
1589
1590         /* generate for each slice */
1591         for (i=0,idx=0; i<slices; i++)
1592         {
1593             GLushort offset = 1+i;                  /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1594             sliceIdx[idx++] = offset+slices;
1595             sliceIdx[idx++] = offset+(stacks+1)*slices;
1596         }
1597
1598         /* draw */
1599         glEnableClientState(GL_VERTEX_ARRAY);
1600         glEnableClientState(GL_NORMAL_ARRAY);
1601
1602         glVertexPointer(3, GL_FLOAT, 0, vertices);
1603         glNormalPointer(GL_FLOAT, 0, normals);
1604         /*draw slices*/
1605         glDrawElements(GL_LINES,slices*2,GL_UNSIGNED_SHORT,sliceIdx);
1606         /*draw stacks*/
1607         for (i=0; i<stacks+1; i++)
1608             glDrawElements(GL_LINE_LOOP, slices,GL_UNSIGNED_SHORT,stackIdx+i*slices);
1609
1610         glDisableClientState(GL_VERTEX_ARRAY);
1611         glDisableClientState(GL_NORMAL_ARRAY);
1612
1613         /* cleanup allocated memory */
1614         free(sliceIdx);
1615         free(stackIdx);
1616     }
1617     else
1618     {
1619         /* First, generate vertex index arrays for drawing with glDrawElements
1620          * All stacks, including top and bottom are covered with a triangle
1621          * strip.
1622          */
1623         GLushort  *stripIdx;
1624         /* Create index vector */
1625         GLushort offset;
1626
1627         /* Allocate buffers for indices, bail out if memory allocation fails */
1628         stripIdx = malloc((slices+1)*2*(stacks+2)*sizeof(GLushort));    /*stacks +2 because of closing off bottom and top */
1629         if (!(stripIdx))
1630         {
1631             free(stripIdx);
1632             fgError("Failed to allocate memory in fghCylinder");
1633         }
1634
1635         /* top stack */
1636         for (j=0, idx=0;  j<slices;  j++, idx+=2)
1637         {
1638             stripIdx[idx  ] = 0;
1639             stripIdx[idx+1] = j+1;              /* 0 is top vertex, 1 is first for first stack */
1640         }
1641         stripIdx[idx  ] = 0;                    /* repeat first slice's idx for closing off shape */
1642         stripIdx[idx+1] = 1;
1643         idx+=2;
1644
1645         /* middle stacks: */
1646         /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1647         for (i=0; i<stacks; i++, idx+=2)
1648         {
1649             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 */
1650             for (j=0; j<slices; j++, idx+=2)
1651             {
1652                 stripIdx[idx  ] = offset+j;
1653                 stripIdx[idx+1] = offset+j+slices;
1654             }
1655             stripIdx[idx  ] = offset;               /* repeat first slice's idx for closing off shape */
1656             stripIdx[idx+1] = offset+slices;
1657         }
1658
1659         /* top stack */
1660         offset = 1+(stacks+2)*slices;
1661         for (j=0; j<slices; j++, idx+=2)
1662         {
1663             stripIdx[idx  ] = offset+j;
1664             stripIdx[idx+1] = nVert-1;              /* zero based index, last element in array (bottom vertex)... */
1665         }
1666         stripIdx[idx  ] = offset;
1667         stripIdx[idx+1] = nVert-1;                  /* repeat first slice's idx for closing off shape */
1668
1669         /* draw */
1670         glEnableClientState(GL_VERTEX_ARRAY);
1671         glEnableClientState(GL_NORMAL_ARRAY);
1672
1673         glVertexPointer(3, GL_FLOAT, 0, vertices);
1674         glNormalPointer(GL_FLOAT, 0, normals);
1675         /*draw stacks*/
1676         for (i=0; i<stacks+2; i++)
1677             glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1678
1679         glDisableClientState(GL_VERTEX_ARRAY);
1680         glDisableClientState(GL_NORMAL_ARRAY);
1681
1682         /* cleanup allocated memory */
1683         free(stripIdx);
1684     }
1685
1686     /* cleanup allocated memory */
1687     free(vertices);
1688     free(normals);
1689 }
1690
1691 static void fghTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings, GLboolean useWireMode )
1692 {
1693     int i,j,idx, nVert;
1694     GLfloat *vertices, *normals;
1695
1696     /* Generate vertices and normals */
1697     fghGenerateTorus((GLfloat)dInnerRadius,(GLfloat)dOuterRadius,nSides,nRings, &vertices,&normals,&nVert);
1698
1699     if (nVert==0)
1700         /* nothing to draw */
1701         return;
1702
1703     if (useWireMode)
1704     {
1705         GLushort  *sideIdx, *ringIdx;
1706         /* First, generate vertex index arrays for drawing with glDrawElements
1707          * We have a bunch of line_loops to draw each side, and a
1708          * bunch for each ring.
1709          */
1710
1711         ringIdx = malloc(nRings*nSides*sizeof(GLushort));
1712         sideIdx = malloc(nSides*nRings*sizeof(GLushort));
1713         if (!(ringIdx) || !(sideIdx))
1714         {
1715             free(ringIdx);
1716             free(sideIdx);
1717             fgError("Failed to allocate memory in fghTorus");
1718         }
1719
1720         /* generate for each ring */
1721         for( j=0,idx=0; j<nRings; j++ )
1722             for( i=0; i<nSides; i++, idx++ )
1723                 ringIdx[idx] = j * nSides + i;
1724
1725         /* generate for each side */
1726         for( i=0,idx=0; i<nSides; i++ )
1727             for( j=0; j<nRings; j++, idx++ )
1728                 sideIdx[idx] = j * nSides + i;
1729
1730         /* draw */
1731         glEnableClientState(GL_VERTEX_ARRAY);
1732         glEnableClientState(GL_NORMAL_ARRAY);
1733
1734         glVertexPointer(3, GL_FLOAT, 0, vertices);
1735         glNormalPointer(GL_FLOAT, 0, normals);
1736         /*draw rings*/
1737         for( i=0; i<nSides; i++ )
1738             glDrawElements(GL_LINE_LOOP,nRings,GL_UNSIGNED_SHORT,ringIdx+i*nRings);
1739         /*draw sides*/
1740         for (i=0; i<nRings; i++)
1741             glDrawElements(GL_LINE_LOOP,nSides,GL_UNSIGNED_SHORT,sideIdx+i*nSides);
1742
1743         glDisableClientState(GL_VERTEX_ARRAY);
1744         glDisableClientState(GL_NORMAL_ARRAY);
1745
1746         /* cleanup allocated memory */
1747         free(sideIdx);
1748         free(ringIdx);
1749     }
1750     else
1751     {
1752         /* clearly, this branch is TODO */
1753         /* First, generate vertex index arrays for drawing with glDrawElements
1754          * All stacks, including top and bottom are covered with a triangle
1755          * strip.
1756          */
1757         GLushort  *stripIdx;
1758
1759         /* Allocate buffers for indices, bail out if memory allocation fails */
1760         //stripIdx = malloc((slices+1)*2*(stacks+2)*sizeof(GLushort));
1761         if (!(stripIdx))
1762         {
1763             free(stripIdx);
1764             fgError("Failed to allocate memory in fghTorus");
1765         }
1766
1767         /* draw */
1768         glEnableClientState(GL_VERTEX_ARRAY);
1769         glEnableClientState(GL_NORMAL_ARRAY);
1770
1771         glVertexPointer(3, GL_FLOAT, 0, vertices);
1772         glNormalPointer(GL_FLOAT, 0, normals);
1773         /*draw stacks*/
1774         //for (i=0; i<stacks+2; i++)
1775           //  glDrawElements(GL_TRIANGLE_STRIP,(slices+1)*2,GL_UNSIGNED_SHORT,stripIdx+i*(slices+1)*2);
1776
1777         glDisableClientState(GL_VERTEX_ARRAY);
1778         glDisableClientState(GL_NORMAL_ARRAY);
1779
1780         /* cleanup allocated memory */
1781         free(stripIdx);
1782     }
1783
1784     /* cleanup allocated memory */
1785     free(vertices);
1786     free(normals);
1787 }
1788
1789
1790 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
1791
1792
1793 /*
1794  * Draws a solid sphere
1795  */
1796 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
1797 {
1798     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
1799
1800     fghSphere( radius, slices, stacks, FALSE );
1801 }
1802
1803 /*
1804  * Draws a wire sphere
1805  */
1806 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
1807 {
1808     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
1809
1810     fghSphere( radius, slices, stacks, TRUE );
1811     
1812 }
1813 #endif /* GL_ES_VERSION_2_0 */
1814
1815 #ifndef EGL_VERSION_1_0
1816 /*
1817  * Draws a solid cone
1818  */
1819 void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks )
1820 {
1821     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
1822
1823     fghCone( base, height, slices, stacks, FALSE );
1824 }
1825
1826 /*
1827  * Draws a wire cone
1828  */
1829 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
1830 {
1831     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1832
1833     fghCone( base, height, slices, stacks, TRUE );
1834 }
1835
1836
1837 /*
1838  * Draws a solid cylinder
1839  */
1840 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
1841 {
1842     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1843
1844     fghCylinder( radius, height, slices, stacks, FALSE );
1845 }
1846
1847 /*
1848  * Draws a wire cylinder
1849  */
1850 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
1851 {
1852     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1853
1854     fghCylinder( radius, height, slices, stacks, TRUE );
1855 }
1856
1857 /*
1858  * Draws a wire torus
1859  */
1860 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1861 {
1862     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1863
1864     fghTorus(dInnerRadius, dOuterRadius, nSides, nRings, TRUE);
1865 }
1866
1867 /*
1868  * Draws a solid torus
1869  */
1870 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1871 {
1872     GLfloat *vertex, *normal;
1873     int    i, j, nVert;
1874
1875     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1876
1877
1878     fghGenerateTorus(
1879         dInnerRadius, dOuterRadius, nSides, nRings,  /*  input */
1880         &vertex, &normal, &nVert                     /* output */
1881         );
1882
1883
1884     for( i=0; i<nSides; i++ )
1885     {
1886         int offset;
1887
1888         int ioff = 3;
1889         if (i==nSides-1)
1890             ioff = -i*3;
1891
1892         glBegin( GL_TRIANGLE_STRIP );
1893         for( j=0; j<nRings; j++ )
1894         {
1895             offset = 3 * ( j * nSides + i ) ;
1896             glNormal3fv( normal + offset );
1897             glVertex3fv( vertex + offset );
1898             glNormal3fv( normal + offset + ioff );
1899             glVertex3fv( vertex + offset + ioff );
1900         }
1901         /* repeat first to close off shape */
1902         offset = 3 * i;
1903         glNormal3fv( normal + offset );
1904         glVertex3fv( vertex + offset );
1905         glNormal3fv( normal + offset + ioff );
1906         glVertex3fv( vertex + offset + ioff );
1907         glEnd();
1908     }
1909
1910     free ( vertex ) ;
1911     free ( normal ) ;
1912 }
1913 #endif /* EGL_VERSION_1_0 */
1914
1915
1916
1917 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1918 /* Macro to generate interface functions */
1919 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1920     void FGAPIENTRY glutWire##nameICaps( void )\
1921     {\
1922         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1923         fgh##nameICaps( TRUE );\
1924     }\
1925     void FGAPIENTRY glutSolid##nameICaps( void )\
1926     {\
1927         FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1928         fgh##nameICaps( FALSE );\
1929     }
1930
1931 void FGAPIENTRY glutWireCube( double dSize )
1932 {
1933     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1934     fghCube( (GLfloat)dSize, TRUE );
1935 }
1936 void FGAPIENTRY glutSolidCube( double dSize )
1937 {
1938     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1939     fghCube( (GLfloat)dSize, FALSE );
1940 }
1941
1942 DECLARE_SHAPE_INTERFACE(Dodecahedron)
1943 DECLARE_SHAPE_INTERFACE(Icosahedron)
1944 DECLARE_SHAPE_INTERFACE(Octahedron)
1945 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron)
1946
1947 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
1948 {
1949     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1950     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, TRUE );
1951 }
1952 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
1953 {
1954     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1955     fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, FALSE );
1956 }
1957
1958 DECLARE_SHAPE_INTERFACE(Tetrahedron)
1959
1960
1961 /*** END OF FILE ***/