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