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