octahedron also done
[freeglut] / src / fg_geometry.c
1 /*
2  * freeglut_geometry.c
3  *
4  * Freeglut geometry rendering methods.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Creation date: Fri Dec 3 1999
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #include <GL/freeglut.h>
29 #include "fg_internal.h"
30
31 /*
32  * 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 /*
148  * In general, we build arrays with all vertices or normals.
149  * We cant compress this and use glDrawElements as all combinations of
150  * vertex and normals are unique.
151  */
152
153 /* -- Cube -- */
154 #define CUBE_NUM_VERT           8
155 #define CUBE_NUM_FACES          6
156 #define CUBE_NUM_VERT_PER_FACE  4
157 #define CUBE_VERT_PER_CUBE      CUBE_NUM_FACES*CUBE_NUM_VERT_PER_FACE
158 #define CUBE_VERT_ELEM_PER_CUBE CUBE_VERT_PER_CUBE*3
159 /* Vertex Coordinates */
160 static GLdouble cube_v[CUBE_NUM_VERT*3] =
161 {
162      .5, .5, .5,
163     -.5, .5, .5,
164     -.5,-.5, .5,
165      .5,-.5, .5,
166      .5,-.5,-.5,
167      .5, .5,-.5,
168     -.5, .5,-.5,
169     -.5,-.5,-.5
170 };
171 /* Normal Vectors */
172 static GLdouble cube_n[CUBE_NUM_FACES*3] =
173 {
174      0.0, 0.0, 1.0,
175      1.0, 0.0, 0.0,
176      0.0, 1.0, 0.0,
177     -1.0, 0.0, 0.0,
178      0.0,-1.0, 0.0,
179      0.0, 0.0,-1.0
180 };
181
182 /* Vertex indices */
183 static GLubyte cube_vi[CUBE_VERT_PER_CUBE] =
184 {
185     0,1,2,3,
186     0,3,4,5,
187     0,5,6,1,
188     1,6,7,2,
189     7,4,3,2,
190     4,7,6,5
191 };
192
193 /* Cache of input to glDrawArrays */
194 static GLboolean cubeCached = FALSE;
195 static GLdouble cube_verts[CUBE_VERT_ELEM_PER_CUBE];
196 static GLdouble cube_norms[CUBE_VERT_ELEM_PER_CUBE];
197
198 static void fghCubeGenerate()
199 {
200     fghGenerateGeometry(CUBE_NUM_FACES, CUBE_NUM_VERT_PER_FACE, cube_v, cube_vi, cube_n, cube_verts, cube_norms);
201 }
202
203 /* -- Octahedron -- */
204 #define OCTAHEDRON_NUM_VERT           6
205 #define OCTAHEDRON_NUM_FACES          8
206 #define OCTAHEDRON_NUM_VERT_PER_FACE  3
207 #define OCTAHEDRON_VERT_PER_OCTA      OCTAHEDRON_NUM_FACES*OCTAHEDRON_NUM_VERT_PER_FACE
208 #define OCTAHEDRON_VERT_ELEM_PER_OCTA OCTAHEDRON_VERT_PER_OCTA*3
209
210 /* Vertex Coordinates */
211 static GLdouble octahedron_v[OCTAHEDRON_NUM_VERT*3] =
212 {
213      1.,  0.,  0.,
214      0.,  1.,  0.,
215      0.,  0.,  1.,
216     -1.,  0.,  0.,
217      0., -1.,  0.,
218      0.,  0., -1.,
219
220 };
221 /* Normal Vectors */
222 static GLdouble octahedron_n[OCTAHEDRON_NUM_FACES*3] =
223 {
224      0.577350269189, 0.577350269189, 0.577350269189,    /* sqrt(1/3) */
225      0.577350269189, 0.577350269189,-0.577350269189,
226      0.577350269189,-0.577350269189, 0.577350269189,
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
233 };
234
235 /* Vertex indices */
236 static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OCTA] =
237 {
238     0, 1, 2,
239     0, 5, 1,
240     0, 2, 4,
241     0, 4, 5,
242     3, 2, 1,
243     3, 1, 5,
244     3, 4, 2,
245     3, 5, 4
246 };
247
248 /* Cache of input to glDrawArrays */
249 static GLboolean octahedronCached = FALSE;
250 static GLdouble octahedron_verts[OCTAHEDRON_VERT_ELEM_PER_OCTA];
251 static GLdouble octahedron_norms[OCTAHEDRON_VERT_ELEM_PER_OCTA];
252
253 static void fghOctahedronGenerate()
254 {
255     fghGenerateGeometry(OCTAHEDRON_NUM_FACES, OCTAHEDRON_NUM_VERT_PER_FACE, octahedron_v, octahedron_vi, octahedron_n, octahedron_verts, octahedron_norms);
256 }
257
258 /* -- Tetrahedron -- */
259 /* Magic Numbers:  r0 = ( 1, 0, 0 )
260  *                 r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
261  *                 r2 = ( -1/3, - sqrt(2) / 3,  sqrt(6) / 3 )
262  *                 r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
263  * |r0| = |r1| = |r2| = |r3| = 1
264  * Distance between any two points is 2 sqrt(6) / 3
265  *
266  * Normals:  The unit normals are simply the negative of the coordinates of the point not on the surface.
267  */
268 #define TETR_NUM_VERT           4
269 #define TETR_NUM_FACES          4
270 #define TETR_NUM_VERT_PER_FACE  3
271 #define TETR_VERT_PER_TETR      TETR_NUM_FACES*TETR_NUM_VERT_PER_FACE
272 #define TETR_VERT_ELEM_PER_TETR TETR_VERT_PER_TETR*3
273
274 /* Vertex Coordinates */
275 static GLdouble tetr_v[TETR_NUM_VERT*3] =
276 {
277                 1.0,             0.0,             0.0,
278     -0.333333333333,  0.942809041582,             0.0,
279     -0.333333333333, -0.471404520791,  0.816496580928,
280     -0.333333333333, -0.471404520791, -0.816496580928
281 };
282 /* Normal Vectors */
283 static GLdouble tetr_n[CUBE_NUM_FACES*3] =
284 {
285     -           1.0,             0.0,             0.0,
286      0.333333333333, -0.942809041582,             0.0,
287      0.333333333333,  0.471404520791, -0.816496580928,
288      0.333333333333,  0.471404520791,  0.816496580928
289 };
290
291 /* Vertex indices */
292 static GLubyte tetr_vi[TETR_VERT_PER_TETR] =
293 {
294     1, 3, 2,
295     0, 2, 3,
296     0, 3, 1,
297     0, 1, 2
298 };
299
300 /* Cache of input to glDrawArrays */
301 static GLboolean tetrCached = FALSE;
302 static GLdouble tetr_verts[TETR_VERT_ELEM_PER_TETR];
303 static GLdouble tetr_norms[TETR_VERT_ELEM_PER_TETR];
304
305 static void fghTetrahedronGenerate()
306 {
307     fghGenerateGeometry(TETR_NUM_FACES, TETR_NUM_VERT_PER_FACE, tetr_v, tetr_vi, tetr_n, tetr_verts, tetr_norms);
308 }
309
310 /* -- Sierpinski Sponge -- */
311 static void fghSierpinskiSpongeGenerate ( int numLevels, GLdouble offset[3], GLdouble scale, GLdouble* vertices, GLdouble* normals )
312 {
313     int i, j;
314     if ( numLevels == 0 )
315     {
316         for (i=0; i<TETR_NUM_FACES; i++)
317         {
318             int normIdx         = i*3;
319             int faceIdxVertIdx  = i*TETR_NUM_VERT_PER_FACE;
320             for (j=0; j<TETR_NUM_VERT_PER_FACE; j++)
321             {
322                 int outIdx  = i*TETR_NUM_VERT_PER_FACE*3+j*3;
323                 int vertIdx = tetr_vi[faceIdxVertIdx+j]*3;
324
325                 vertices[outIdx  ] = offset[0] + scale * tetr_v[vertIdx  ];
326                 vertices[outIdx+1] = offset[1] + scale * tetr_v[vertIdx+1];
327                 vertices[outIdx+2] = offset[2] + scale * tetr_v[vertIdx+2];
328
329                 normals [outIdx  ] = tetr_n[normIdx  ];
330                 normals [outIdx+1] = tetr_n[normIdx+1];
331                 normals [outIdx+2] = tetr_n[normIdx+2];
332             }
333         }
334     }
335     else if ( numLevels > 0 )
336     {
337         GLdouble local_offset[3] ;  /* Use a local variable to avoid buildup of roundoff errors */
338         unsigned int stride = ipow(4,--numLevels)*TETR_VERT_ELEM_PER_TETR;
339         scale /= 2.0 ;
340         for ( i = 0 ; i < TETR_NUM_FACES ; i++ )
341         {
342             int idx         = i*3;
343             local_offset[0] = offset[0] + scale * tetr_v[idx  ];
344             local_offset[1] = offset[1] + scale * tetr_v[idx+1];
345             local_offset[2] = offset[2] + scale * tetr_v[idx+2];
346             fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride );
347         }
348     }
349 }
350
351 /* -- Now the various shapes involving circles -- */
352 /*
353  * Compute lookup table of cos and sin values forming a cirle
354  *
355  * Notes:
356  *    It is the responsibility of the caller to free these tables
357  *    The size of the table is (n+1) to form a connected loop
358  *    The last entry is exactly the same as the first
359  *    The sign of n can be flipped to get the reverse loop
360  */
361 static void fghCircleTable(double **sint,double **cost,const int n)
362 {
363     int i;
364
365     /* Table size, the sign of n flips the circle direction */
366
367     const int size = abs(n);
368
369     /* Determine the angle between samples */
370
371     const double angle = 2*M_PI/(double)( ( n == 0 ) ? 1 : n );
372
373     /* Allocate memory for n samples, plus duplicate of first entry at the end */
374
375     *sint = (double *) calloc(sizeof(double), size+1);
376     *cost = (double *) calloc(sizeof(double), size+1);
377
378     /* Bail out if memory allocation fails, fgError never returns */
379
380     if (!(*sint) || !(*cost))
381     {
382         free(*sint);
383         free(*cost);
384         fgError("Failed to allocate memory in fghCircleTable");
385     }
386
387     /* Compute cos and sin around the circle */
388
389     (*sint)[0] = 0.0;
390     (*cost)[0] = 1.0;
391
392     for (i=1; i<size; i++)
393     {
394         (*sint)[i] = sin(angle*i);
395         (*cost)[i] = cos(angle*i);
396     }
397
398     /* Last sample is duplicate of the first */
399
400     (*sint)[size] = (*sint)[0];
401     (*cost)[size] = (*cost)[0];
402 }
403
404
405 /* -- INTERNAL DRAWING functions to avoid code duplication ------------- */
406
407 static void fghCube( GLdouble dSize, GLboolean useWireMode )
408 {
409     if (!cubeCached)
410     {
411         fghCubeGenerate();
412         cubeCached = TRUE;
413     }
414
415     if (dSize!=1.)
416     {
417         int i;
418
419         /* Need to build new vertex list containing vertices for cube of different size */
420         GLdouble *vertices = malloc(CUBE_VERT_ELEM_PER_CUBE * sizeof(GLdouble));
421         for (i=0; i<CUBE_VERT_ELEM_PER_CUBE; i++)
422             vertices[i] = dSize*cube_verts[i];
423
424         fghDrawGeometry(GL_QUADS,vertices  ,cube_norms,CUBE_VERT_PER_CUBE,useWireMode);
425     }
426     else
427         fghDrawGeometry(GL_QUADS,cube_verts,cube_norms,CUBE_VERT_PER_CUBE,useWireMode);
428 }
429
430 static void fghOctahedron( GLboolean useWireMode )
431 {
432     if (!octahedronCached)
433     {
434         fghOctahedronGenerate();
435         octahedronCached = TRUE;
436     }
437
438     fghDrawGeometry(GL_TRIANGLES,octahedron_verts,octahedron_norms,OCTAHEDRON_VERT_PER_OCTA,useWireMode);
439 }
440
441 static void fghTetrahedron( GLboolean useWireMode )
442 {
443     if (!tetrCached)
444     {
445         fghTetrahedronGenerate();
446         tetrCached = TRUE;
447     }
448
449     fghDrawGeometry(GL_TRIANGLES,tetr_verts,tetr_norms,TETR_VERT_PER_TETR,useWireMode);
450 }
451
452 static void fghSierpinskiSponge ( int numLevels, GLdouble offset[3], GLdouble scale, GLboolean useWireMode )
453 {
454     GLdouble *vertices;
455     GLdouble * normals;
456     GLsizei    numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
457     GLsizei    numVert = numTetr*TETR_VERT_PER_TETR;
458
459     if (numTetr)
460     {
461         /* Allocate memory */
462         vertices = malloc(numVert*3 * sizeof(GLdouble));
463         normals  = malloc(numVert*3 * sizeof(GLdouble));
464
465         /* Generate elements */
466         fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
467
468         /* Draw and cleanup */
469         fghDrawGeometry(GL_TRIANGLES,vertices,normals,numVert,useWireMode);
470         free(vertices);
471         free(normals );
472     }
473 }
474
475
476 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
477
478
479 /*
480  * Draws a solid sphere
481  */
482 void FGAPIENTRY glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
483 {
484     int i,j;
485
486     /* Adjust z and radius as stacks are drawn. */
487
488     double z0,z1;
489     double r0,r1;
490
491     /* Pre-computed circle */
492
493     double *sint1,*cost1;
494     double *sint2,*cost2;
495
496     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
497
498     fghCircleTable(&sint1,&cost1,-slices);
499     fghCircleTable(&sint2,&cost2,stacks*2);
500
501     /* The top stack is covered with a triangle fan */
502
503     z0 = 1.0;
504     z1 = cost2[(stacks>0)?1:0];
505     r0 = 0.0;
506     r1 = sint2[(stacks>0)?1:0];
507
508     glBegin(GL_TRIANGLE_FAN);
509
510         glNormal3d(0,0,1);
511         glVertex3d(0,0,radius);
512
513         for (j=slices; j>=0; j--)
514         {
515             glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
516             glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
517         }
518
519     glEnd();
520
521     /* Cover each stack with a quad strip, except the top and bottom stacks */
522
523     for( i=1; i<stacks-1; i++ )
524     {
525         z0 = z1; z1 = cost2[i+1];
526         r0 = r1; r1 = sint2[i+1];
527
528         glBegin(GL_QUAD_STRIP);
529
530             for(j=0; j<=slices; j++)
531             {
532                 glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
533                 glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
534                 glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
535                 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
536             }
537
538         glEnd();
539     }
540
541     /* The bottom stack is covered with a triangle fan */
542
543     z0 = z1;
544     r0 = r1;
545
546     glBegin(GL_TRIANGLE_FAN);
547
548         glNormal3d(0,0,-1);
549         glVertex3d(0,0,-radius);
550
551         for (j=0; j<=slices; j++)
552         {
553             glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
554             glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
555         }
556
557     glEnd();
558
559     /* Release sin and cos tables */
560
561     free(sint1);
562     free(cost1);
563     free(sint2);
564     free(cost2);
565 }
566
567 /*
568  * Draws a wire sphere
569  */
570 void FGAPIENTRY glutWireSphere(GLdouble radius, GLint slices, GLint stacks)
571 {
572     int i,j;
573
574     /* Adjust z and radius as stacks and slices are drawn. */
575
576     double r;
577     double x,y,z;
578
579     /* Pre-computed circle */
580
581     double *sint1,*cost1;
582     double *sint2,*cost2;
583
584     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
585
586     fghCircleTable(&sint1,&cost1,-slices  );
587     fghCircleTable(&sint2,&cost2, stacks*2);
588
589     /* Draw a line loop for each stack */
590
591     for (i=1; i<stacks; i++)
592     {
593         z = cost2[i];
594         r = sint2[i];
595
596         glBegin(GL_LINE_LOOP);
597
598             for(j=0; j<=slices; j++)
599             {
600                 x = cost1[j];
601                 y = sint1[j];
602
603                 glNormal3d(x,y,z);
604                 glVertex3d(x*r*radius,y*r*radius,z*radius);
605             }
606
607         glEnd();
608     }
609
610     /* Draw a line loop for each slice */
611
612     for (i=0; i<slices; i++)
613     {
614         glBegin(GL_LINE_STRIP);
615
616             for(j=0; j<=stacks; j++)
617             {
618                 x = cost1[i]*sint2[j];
619                 y = sint1[i]*sint2[j];
620                 z = cost2[j];
621
622                 glNormal3d(x,y,z);
623                 glVertex3d(x*radius,y*radius,z*radius);
624             }
625
626         glEnd();
627     }
628
629     /* Release sin and cos tables */
630
631     free(sint1);
632     free(cost1);
633     free(sint2);
634     free(cost2);
635 }
636
637 /*
638  * Draws a solid cone
639  */
640 void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
641 {
642     int i,j;
643
644     /* Step in z and radius as stacks are drawn. */
645
646     double z0,z1;
647     double r0,r1;
648
649     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
650     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
651
652     /* Scaling factors for vertex normals */
653
654     const double cosn = ( height / sqrt ( height * height + base * base ));
655     const double sinn = ( base   / sqrt ( height * height + base * base ));
656
657     /* Pre-computed circle */
658
659     double *sint,*cost;
660
661     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
662
663     fghCircleTable(&sint,&cost,-slices);
664
665     /* Cover the circular base with a triangle fan... */
666
667     z0 = 0.0;
668     z1 = zStep;
669
670     r0 = base;
671     r1 = r0 - rStep;
672
673     glBegin(GL_TRIANGLE_FAN);
674
675         glNormal3d(0.0,0.0,-1.0);
676         glVertex3d(0.0,0.0, z0 );
677
678         for (j=0; j<=slices; j++)
679             glVertex3d(cost[j]*r0, sint[j]*r0, z0);
680
681     glEnd();
682
683     /* Cover each stack with a quad strip, except the top stack */
684
685     for( i=0; i<stacks-1; i++ )
686     {
687         glBegin(GL_QUAD_STRIP);
688
689             for(j=0; j<=slices; j++)
690             {
691                 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
692                 glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
693                 glVertex3d(cost[j]*r1,   sint[j]*r1,   z1  );
694             }
695
696             z0 = z1; z1 += zStep;
697             r0 = r1; r1 -= rStep;
698
699         glEnd();
700     }
701
702     /* The top stack is covered with individual triangles */
703
704     glBegin(GL_TRIANGLES);
705
706         glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
707
708         for (j=0; j<slices; j++)
709         {
710             glVertex3d(cost[j+0]*r0,   sint[j+0]*r0,   z0    );
711             glVertex3d(0,              0,              height);
712             glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn  );
713             glVertex3d(cost[j+1]*r0,   sint[j+1]*r0,   z0    );
714         }
715
716     glEnd();
717
718     /* Release sin and cos tables */
719
720     free(sint);
721     free(cost);
722 }
723
724 /*
725  * Draws a wire cone
726  */
727 void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks)
728 {
729     int i,j;
730
731     /* Step in z and radius as stacks are drawn. */
732
733     double z = 0.0;
734     double r = base;
735
736     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
737     const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
738
739     /* Scaling factors for vertex normals */
740
741     const double cosn = ( height / sqrt ( height * height + base * base ));
742     const double sinn = ( base   / sqrt ( height * height + base * base ));
743
744     /* Pre-computed circle */
745
746     double *sint,*cost;
747
748     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
749
750     fghCircleTable(&sint,&cost,-slices);
751
752     /* Draw the stacks... */
753
754     for (i=0; i<stacks; i++)
755     {
756         glBegin(GL_LINE_LOOP);
757
758             for( j=0; j<slices; j++ )
759             {
760                 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
761                 glVertex3d(cost[j]*r,    sint[j]*r,    z   );
762             }
763
764         glEnd();
765
766         z += zStep;
767         r -= rStep;
768     }
769
770     /* Draw the slices */
771
772     r = base;
773
774     glBegin(GL_LINES);
775
776         for (j=0; j<slices; j++)
777         {
778             glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn  );
779             glVertex3d(cost[j]*r,    sint[j]*r,    0.0   );
780             glVertex3d(0.0,          0.0,          height);
781         }
782
783     glEnd();
784
785     /* Release sin and cos tables */
786
787     free(sint);
788     free(cost);
789 }
790
791
792 /*
793  * Draws a solid cylinder
794  */
795 void FGAPIENTRY glutSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
796 {
797     int i,j;
798
799     /* Step in z and radius as stacks are drawn. */
800
801     double z0,z1;
802     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
803
804     /* Pre-computed circle */
805
806     double *sint,*cost;
807
808     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
809
810     fghCircleTable(&sint,&cost,-slices);
811
812     /* Cover the base and top */
813
814     glBegin(GL_TRIANGLE_FAN);
815         glNormal3d(0.0, 0.0, -1.0 );
816         glVertex3d(0.0, 0.0,  0.0 );
817         for (j=0; j<=slices; j++)
818           glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
819     glEnd();
820
821     glBegin(GL_TRIANGLE_FAN);
822         glNormal3d(0.0, 0.0, 1.0   );
823         glVertex3d(0.0, 0.0, height);
824         for (j=slices; j>=0; j--)
825           glVertex3d(cost[j]*radius, sint[j]*radius, height);
826     glEnd();
827
828     /* Do the stacks */
829
830     z0 = 0.0;
831     z1 = zStep;
832
833     for (i=1; i<=stacks; i++)
834     {
835         if (i==stacks)
836             z1 = height;
837
838         glBegin(GL_QUAD_STRIP);
839             for (j=0; j<=slices; j++ )
840             {
841                 glNormal3d(cost[j],        sint[j],        0.0 );
842                 glVertex3d(cost[j]*radius, sint[j]*radius, z0  );
843                 glVertex3d(cost[j]*radius, sint[j]*radius, z1  );
844             }
845         glEnd();
846
847         z0 = z1; z1 += zStep;
848     }
849
850     /* Release sin and cos tables */
851
852     free(sint);
853     free(cost);
854 }
855
856 /*
857  * Draws a wire cylinder
858  */
859 void FGAPIENTRY glutWireCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
860 {
861     int i,j;
862
863     /* Step in z and radius as stacks are drawn. */
864
865           double z = 0.0;
866     const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
867
868     /* Pre-computed circle */
869
870     double *sint,*cost;
871
872     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
873
874     fghCircleTable(&sint,&cost,-slices);
875
876     /* Draw the stacks... */
877
878     for (i=0; i<=stacks; i++)
879     {
880         if (i==stacks)
881             z = height;
882
883         glBegin(GL_LINE_LOOP);
884
885             for( j=0; j<slices; j++ )
886             {
887                 glNormal3d(cost[j],        sint[j],        0.0);
888                 glVertex3d(cost[j]*radius, sint[j]*radius, z  );
889             }
890
891         glEnd();
892
893         z += zStep;
894     }
895
896     /* Draw the slices */
897
898     glBegin(GL_LINES);
899
900         for (j=0; j<slices; j++)
901         {
902             glNormal3d(cost[j],        sint[j],        0.0   );
903             glVertex3d(cost[j]*radius, sint[j]*radius, 0.0   );
904             glVertex3d(cost[j]*radius, sint[j]*radius, height);
905         }
906
907     glEnd();
908
909     /* Release sin and cos tables */
910
911     free(sint);
912     free(cost);
913 }
914
915 /*
916  * Draws a wire torus
917  */
918 void FGAPIENTRY glutWireTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
919 {
920   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
921   double *vertex, *normal;
922   int    i, j;
923   double spsi, cpsi, sphi, cphi ;
924
925   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
926
927   if ( nSides < 1 ) nSides = 1;
928   if ( nRings < 1 ) nRings = 1;
929
930   /* Allocate the vertices array */
931   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
932   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
933
934   glPushMatrix();
935
936   dpsi =  2.0 * M_PI / (double)nRings ;
937   dphi = -2.0 * M_PI / (double)nSides ;
938   psi  = 0.0;
939
940   for( j=0; j<nRings; j++ )
941   {
942     cpsi = cos ( psi ) ;
943     spsi = sin ( psi ) ;
944     phi = 0.0;
945
946     for( i=0; i<nSides; i++ )
947     {
948       int offset = 3 * ( j * nSides + i ) ;
949       cphi = cos ( phi ) ;
950       sphi = sin ( phi ) ;
951       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
952       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
953       *(vertex + offset + 2) =                    sphi * iradius  ;
954       *(normal + offset + 0) = cpsi * cphi ;
955       *(normal + offset + 1) = spsi * cphi ;
956       *(normal + offset + 2) =        sphi ;
957       phi += dphi;
958     }
959
960     psi += dpsi;
961   }
962
963   for( i=0; i<nSides; i++ )
964   {
965     glBegin( GL_LINE_LOOP );
966
967     for( j=0; j<nRings; j++ )
968     {
969       int offset = 3 * ( j * nSides + i ) ;
970       glNormal3dv( normal + offset );
971       glVertex3dv( vertex + offset );
972     }
973
974     glEnd();
975   }
976
977   for( j=0; j<nRings; j++ )
978   {
979     glBegin(GL_LINE_LOOP);
980
981     for( i=0; i<nSides; i++ )
982     {
983       int offset = 3 * ( j * nSides + i ) ;
984       glNormal3dv( normal + offset );
985       glVertex3dv( vertex + offset );
986     }
987
988     glEnd();
989   }
990
991   free ( vertex ) ;
992   free ( normal ) ;
993   glPopMatrix();
994 }
995
996 /*
997  * Draws a solid torus
998  */
999 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1000 {
1001   double  iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1002   double *vertex, *normal;
1003   int    i, j;
1004   double spsi, cpsi, sphi, cphi ;
1005
1006   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1007
1008   if ( nSides < 1 ) nSides = 1;
1009   if ( nRings < 1 ) nRings = 1;
1010
1011   /* Increment the number of sides and rings to allow for one more point than surface */
1012   nSides ++ ;
1013   nRings ++ ;
1014
1015   /* Allocate the vertices array */
1016   vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1017   normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1018
1019   glPushMatrix();
1020
1021   dpsi =  2.0 * M_PI / (double)(nRings - 1) ;
1022   dphi = -2.0 * M_PI / (double)(nSides - 1) ;
1023   psi  = 0.0;
1024
1025   for( j=0; j<nRings; j++ )
1026   {
1027     cpsi = cos ( psi ) ;
1028     spsi = sin ( psi ) ;
1029     phi = 0.0;
1030
1031     for( i=0; i<nSides; i++ )
1032     {
1033       int offset = 3 * ( j * nSides + i ) ;
1034       cphi = cos ( phi ) ;
1035       sphi = sin ( phi ) ;
1036       *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1037       *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1038       *(vertex + offset + 2) =                    sphi * iradius  ;
1039       *(normal + offset + 0) = cpsi * cphi ;
1040       *(normal + offset + 1) = spsi * cphi ;
1041       *(normal + offset + 2) =        sphi ;
1042       phi += dphi;
1043     }
1044
1045     psi += dpsi;
1046   }
1047
1048     glBegin( GL_QUADS );
1049   for( i=0; i<nSides-1; i++ )
1050   {
1051     for( j=0; j<nRings-1; j++ )
1052     {
1053       int offset = 3 * ( j * nSides + i ) ;
1054       glNormal3dv( normal + offset );
1055       glVertex3dv( vertex + offset );
1056       glNormal3dv( normal + offset + 3 );
1057       glVertex3dv( vertex + offset + 3 );
1058       glNormal3dv( normal + offset + 3 * nSides + 3 );
1059       glVertex3dv( vertex + offset + 3 * nSides + 3 );
1060       glNormal3dv( normal + offset + 3 * nSides );
1061       glVertex3dv( vertex + offset + 3 * nSides );
1062     }
1063   }
1064
1065   glEnd();
1066
1067   free ( vertex ) ;
1068   free ( normal ) ;
1069   glPopMatrix();
1070 }
1071
1072 /*
1073  *
1074  */
1075 void FGAPIENTRY glutWireDodecahedron( void )
1076 {
1077   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireDodecahedron" );
1078
1079   /* Magic Numbers:  It is possible to create a dodecahedron by attaching two pentagons to each face of
1080    * of a cube.  The coordinates of the points are:
1081    *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
1082    * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2  or
1083    *       x = 0.61803398875 and z = 1.61803398875.
1084    */
1085   glBegin ( GL_LINE_LOOP ) ;
1086   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 ) ;
1087   glEnd () ;
1088   glBegin ( GL_LINE_LOOP ) ;
1089   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 ) ;
1090   glEnd () ;
1091   glBegin ( GL_LINE_LOOP ) ;
1092   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 ) ;
1093   glEnd () ;
1094   glBegin ( GL_LINE_LOOP ) ;
1095   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 ) ;
1096   glEnd () ;
1097
1098   glBegin ( GL_LINE_LOOP ) ;
1099   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 ) ;
1100   glEnd () ;
1101   glBegin ( GL_LINE_LOOP ) ;
1102   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 ) ;
1103   glEnd () ;
1104   glBegin ( GL_LINE_LOOP ) ;
1105   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 ) ;
1106   glEnd () ;
1107   glBegin ( GL_LINE_LOOP ) ;
1108   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 ) ;
1109   glEnd () ;
1110
1111   glBegin ( GL_LINE_LOOP ) ;
1112   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 ) ;
1113   glEnd () ;
1114   glBegin ( GL_LINE_LOOP ) ;
1115   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 ) ;
1116   glEnd () ;
1117   glBegin ( GL_LINE_LOOP ) ;
1118   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 ) ;
1119   glEnd () ;
1120   glBegin ( GL_LINE_LOOP ) ;
1121   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 ) ;
1122   glEnd () ;
1123 }
1124
1125 /*
1126  *
1127  */
1128 void FGAPIENTRY glutSolidDodecahedron( void )
1129 {
1130   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidDodecahedron" );
1131
1132   /* Magic Numbers:  It is possible to create a dodecahedron by attaching two pentagons to each face of
1133    * of a cube.  The coordinates of the points are:
1134    *   (+-x,0, z); (+-1, 1, 1); (0, z, x )
1135    * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
1136    *       x = 0.61803398875 and z = 1.61803398875.
1137    */
1138   glBegin ( GL_POLYGON ) ;
1139   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 ) ;
1140   glEnd () ;
1141   glBegin ( GL_POLYGON ) ;
1142   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 ) ;
1143   glEnd () ;
1144   glBegin ( GL_POLYGON ) ;
1145   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 ) ;
1146   glEnd () ;
1147   glBegin ( GL_POLYGON ) ;
1148   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 ) ;
1149   glEnd () ;
1150
1151   glBegin ( GL_POLYGON ) ;
1152   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 ) ;
1153   glEnd () ;
1154   glBegin ( GL_POLYGON ) ;
1155   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 ) ;
1156   glEnd () ;
1157   glBegin ( GL_POLYGON ) ;
1158   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 ) ;
1159   glEnd () ;
1160   glBegin ( GL_POLYGON ) ;
1161   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 ) ;
1162   glEnd () ;
1163
1164   glBegin ( GL_POLYGON ) ;
1165   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 ) ;
1166   glEnd () ;
1167   glBegin ( GL_POLYGON ) ;
1168   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 ) ;
1169   glEnd () ;
1170   glBegin ( GL_POLYGON ) ;
1171   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 ) ;
1172   glEnd () ;
1173   glBegin ( GL_POLYGON ) ;
1174   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 ) ;
1175   glEnd () ;
1176 }
1177
1178 /*
1179  *
1180  */
1181 static double icos_r[12][3] = {
1182     {  1.0,             0.0,             0.0            },
1183     {  0.447213595500,  0.894427191000,  0.0            },
1184     {  0.447213595500,  0.276393202252,  0.850650808354 },
1185     {  0.447213595500, -0.723606797748,  0.525731112119 },
1186     {  0.447213595500, -0.723606797748, -0.525731112119 },
1187     {  0.447213595500,  0.276393202252, -0.850650808354 },
1188     { -0.447213595500, -0.894427191000,  0.0 },
1189     { -0.447213595500, -0.276393202252,  0.850650808354 },
1190     { -0.447213595500,  0.723606797748,  0.525731112119 },
1191     { -0.447213595500,  0.723606797748, -0.525731112119 },
1192     { -0.447213595500, -0.276393202252, -0.850650808354 },
1193     { -1.0,             0.0,             0.0            }
1194 };
1195
1196 static int icos_v [20][3] = {
1197     {  0,  1,  2 },
1198     {  0,  2,  3 },
1199     {  0,  3,  4 },
1200     {  0,  4,  5 },
1201     {  0,  5,  1 },
1202     {  1,  8,  2 },
1203     {  2,  7,  3 },
1204     {  3,  6,  4 },
1205     {  4, 10,  5 },
1206     {  5,  9,  1 },
1207     {  1,  9,  8 },
1208     {  2,  8,  7 },
1209     {  3,  7,  6 },
1210     {  4,  6, 10 },
1211     {  5, 10,  9 },
1212     { 11,  9, 10 },
1213     { 11,  8,  9 },
1214     { 11,  7,  8 },
1215     { 11,  6,  7 },
1216     { 11, 10,  6 }
1217 };
1218
1219 void FGAPIENTRY glutWireIcosahedron( void )
1220 {
1221   int i ;
1222
1223   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireIcosahedron" );
1224
1225   for ( i = 0; i < 20; i++ )
1226   {
1227     double normal[3] ;
1228     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] ) ;
1229     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] ) ;
1230     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] ) ;
1231     glBegin ( GL_LINE_LOOP ) ;
1232       glNormal3dv ( normal ) ;
1233       glVertex3dv ( icos_r[icos_v[i][0]] ) ;
1234       glVertex3dv ( icos_r[icos_v[i][1]] ) ;
1235       glVertex3dv ( icos_r[icos_v[i][2]] ) ;
1236     glEnd () ;
1237   }
1238 }
1239
1240 /*
1241  *
1242  */
1243 void FGAPIENTRY glutSolidIcosahedron( void )
1244 {
1245   int i ;
1246
1247   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidIcosahedron" );
1248
1249   glBegin ( GL_TRIANGLES ) ;
1250   for ( i = 0; i < 20; i++ )
1251   {
1252     double normal[3] ;
1253     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] ) ;
1254     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] ) ;
1255     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] ) ;
1256       glNormal3dv ( normal ) ;
1257       glVertex3dv ( icos_r[icos_v[i][0]] ) ;
1258       glVertex3dv ( icos_r[icos_v[i][1]] ) ;
1259       glVertex3dv ( icos_r[icos_v[i][2]] ) ;
1260   }
1261
1262   glEnd () ;
1263 }
1264
1265 /*
1266  *
1267  */
1268 static double rdod_r[14][3] = {
1269     {  0.0,             0.0,             1.0 },
1270     {  0.707106781187,  0.000000000000,  0.5 },
1271     {  0.000000000000,  0.707106781187,  0.5 },
1272     { -0.707106781187,  0.000000000000,  0.5 },
1273     {  0.000000000000, -0.707106781187,  0.5 },
1274     {  0.707106781187,  0.707106781187,  0.0 },
1275     { -0.707106781187,  0.707106781187,  0.0 },
1276     { -0.707106781187, -0.707106781187,  0.0 },
1277     {  0.707106781187, -0.707106781187,  0.0 },
1278     {  0.707106781187,  0.000000000000, -0.5 },
1279     {  0.000000000000,  0.707106781187, -0.5 },
1280     { -0.707106781187,  0.000000000000, -0.5 },
1281     {  0.000000000000, -0.707106781187, -0.5 },
1282     {  0.0,             0.0,            -1.0 }
1283 } ;
1284
1285 static int rdod_v [12][4] = {
1286     { 0,  1,  5,  2 },
1287     { 0,  2,  6,  3 },
1288     { 0,  3,  7,  4 },
1289     { 0,  4,  8,  1 },
1290     { 5, 10,  6,  2 },
1291     { 6, 11,  7,  3 },
1292     { 7, 12,  8,  4 },
1293     { 8,  9,  5,  1 },
1294     { 5,  9, 13, 10 },
1295     { 6, 10, 13, 11 },
1296     { 7, 11, 13, 12 },
1297     { 8, 12, 13,  9 }
1298 };
1299
1300 static double rdod_n[12][3] = {
1301     {  0.353553390594,  0.353553390594,  0.5 },
1302     { -0.353553390594,  0.353553390594,  0.5 },
1303     { -0.353553390594, -0.353553390594,  0.5 },
1304     {  0.353553390594, -0.353553390594,  0.5 },
1305     {  0.000000000000,  1.000000000000,  0.0 },
1306     { -1.000000000000,  0.000000000000,  0.0 },
1307     {  0.000000000000, -1.000000000000,  0.0 },
1308     {  1.000000000000,  0.000000000000,  0.0 },
1309     {  0.353553390594,  0.353553390594, -0.5 },
1310     { -0.353553390594,  0.353553390594, -0.5 },
1311     { -0.353553390594, -0.353553390594, -0.5 },
1312     {  0.353553390594, -0.353553390594, -0.5 }
1313 };
1314
1315 void FGAPIENTRY glutWireRhombicDodecahedron( void )
1316 {
1317   int i ;
1318
1319   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireRhombicDodecahedron" );
1320
1321   for ( i = 0; i < 12; i++ )
1322   {
1323     glBegin ( GL_LINE_LOOP ) ;
1324       glNormal3dv ( rdod_n[i] ) ;
1325       glVertex3dv ( rdod_r[rdod_v[i][0]] ) ;
1326       glVertex3dv ( rdod_r[rdod_v[i][1]] ) ;
1327       glVertex3dv ( rdod_r[rdod_v[i][2]] ) ;
1328       glVertex3dv ( rdod_r[rdod_v[i][3]] ) ;
1329     glEnd () ;
1330   }
1331 }
1332
1333 /*
1334  *
1335  */
1336 void FGAPIENTRY glutSolidRhombicDodecahedron( void )
1337 {
1338   int i ;
1339
1340   FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidRhombicDodecahedron" );
1341
1342   glBegin ( GL_QUADS ) ;
1343   for ( i = 0; i < 12; i++ )
1344   {
1345       glNormal3dv ( rdod_n[i] ) ;
1346       glVertex3dv ( rdod_r[rdod_v[i][0]] ) ;
1347       glVertex3dv ( rdod_r[rdod_v[i][1]] ) ;
1348       glVertex3dv ( rdod_r[rdod_v[i][2]] ) ;
1349       glVertex3dv ( rdod_r[rdod_v[i][3]] ) ;
1350   }
1351
1352   glEnd () ;
1353 }
1354
1355
1356
1357 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1358 /*
1359  * Draws a wireframed cube.
1360  */
1361 void FGAPIENTRY glutWireCube( GLdouble dSize )
1362 {
1363     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1364     fghCube( dSize, TRUE );
1365 }
1366 void FGAPIENTRY glutSolidCube( GLdouble dSize )
1367 {
1368     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1369     fghCube( dSize, FALSE );
1370 }
1371
1372 void FGAPIENTRY glutWireOctahedron( void )
1373 {
1374     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireOctahedron" );
1375     fghOctahedron( TRUE );
1376 }
1377 void FGAPIENTRY glutSolidOctahedron( void )
1378 {
1379     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidOctahedron" );
1380     fghOctahedron( FALSE );
1381 }
1382
1383 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1384 {
1385     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1386     fghSierpinskiSponge ( num_levels, offset, scale, TRUE );
1387 }
1388 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1389 {
1390     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1391     fghSierpinskiSponge ( num_levels, offset, scale, FALSE );
1392 }
1393
1394 void FGAPIENTRY glutWireTetrahedron( void )
1395 {
1396     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTetrahedron" );
1397     fghTetrahedron( TRUE );
1398 }
1399 void FGAPIENTRY glutSolidTetrahedron( void )
1400 {
1401     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTetrahedron" );
1402     fghTetrahedron( FALSE );
1403 }
1404
1405
1406 /*** END OF FILE ***/