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