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