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