4 * Freeglut geometry rendering methods.
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
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:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
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.
28 #include <GL/freeglut.h>
29 #include "fg_internal.h"
33 * Need more types of polyhedra? See CPolyhedron in MRPT
35 * TODO BEFORE THE STABLE RELEASE:
39 * Following functions have been contributed by Andreas Umbach.
41 * glutWireCube() -- looks OK
42 * glutSolidCube() -- OK
44 * Those functions have been implemented by John Fay.
46 * glutWireTorus() -- looks OK
47 * glutSolidTorus() -- looks OK
48 * glutWireDodecahedron() -- looks OK
49 * glutSolidDodecahedron() -- looks OK
50 * glutWireOctahedron() -- looks OK
51 * glutSolidOctahedron() -- looks OK
52 * glutWireTetrahedron() -- looks OK
53 * glutSolidTetrahedron() -- looks OK
54 * glutWireIcosahedron() -- looks OK
55 * glutSolidIcosahedron() -- looks OK
57 * The Following functions have been updated by Nigel Stewart, based
58 * on FreeGLUT 2.0.0 implementations:
60 * glutWireSphere() -- looks OK
61 * glutSolidSphere() -- looks OK
62 * glutWireCone() -- looks OK
63 * glutSolidCone() -- looks OK
67 /* General function for drawing geometry. As for all geometry we have no
68 * redundancy (or hardly any in the case of cones and cylinders) in terms
69 * of the vertex/normal combinations, we just use glDrawArrays.
70 * useWireMode controls the drawing of solids (false) or wire frame
71 * versions (TRUE) of the geometry you pass
73 static void fghDrawGeometry(GLenum vertexMode, GLdouble *vertices, GLdouble *normals, GLboolean *edgeFlags, GLsizei numVertices, GLboolean useWireMode)
77 glPushAttrib(GL_POLYGON_BIT);
78 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
83 glEnableClientState(GL_VERTEX_ARRAY);
84 glEnableClientState(GL_NORMAL_ARRAY);
86 glEnableClientState(GL_EDGE_FLAG_ARRAY);
88 glVertexPointer(3, GL_DOUBLE, 0, vertices);
89 glNormalPointer(GL_DOUBLE, 0, normals);
91 glEdgeFlagPointer(0,edgeFlags);
92 glDrawArrays(vertexMode, 0, numVertices);
94 glDisableClientState(GL_VERTEX_ARRAY);
95 glDisableClientState(GL_NORMAL_ARRAY);
97 glDisableClientState(GL_EDGE_FLAG_ARRAY);
103 for(i=0; i<numVertices; i++)
105 glEdgeFlag(edgeFlags[i]);
106 glNormal3dv(normals+i*3);
107 printf("n(%i) = (%1.4f,%1.4f,%1.4f)\n",i,*(normals+i*3),*(normals+i*3+1),*(normals+i*3+2));
108 glVertex3dv(vertices+i*3);
109 printf("v(%i) = (%1.4f,%1.4f,%1.4f)\n",i,*(vertices+i*3),*(vertices+i*3+1),*(vertices+i*3+2));
120 /* triangle decomposition and associated edgeFlags
121 * be careful to keep winding of all triangles counter-clockwise,
122 * assuming that input has correct winding...
124 static GLubyte vertSamp3[3] = {0,1,2};
125 static GLubyte vertSamp4[6] = {0,1,2, 0,2,3}; /* quad : 4 input vertices, 6 output (2 triangles) */
126 static GLubyte vertSamp5[9] = {0,1,2, 0,2,4, 4,2,3}; /* pentagon: 5 input vertices, 9 output (3 triangles) */
127 static GLboolean edgeFlag3[3] = {1,1,1}; /* triangles remain triangles, all edges are external */
128 static GLboolean edgeFlag4[6] = {1,1,0, 0,1,1};
129 static GLboolean edgeFlag5[9] = {1,1,0, 0,0,1, 0,1,1};
131 static void fghGenerateGeometryWithEdgeFlag(int numFaces, int numEdgePerFaceIn, GLdouble *vertices, GLubyte *vertIndices, GLdouble *normals, GLdouble *vertOut, GLdouble *normOut, GLboolean *edgeFlagsOut)
133 int i,j,numEdgePerFaceOut;
134 GLubyte *vertSamps = NULL;
135 GLboolean *edgeFlags = NULL;
136 switch (numEdgePerFaceIn)
139 vertSamps = vertSamp3;
140 edgeFlags = edgeFlag3;
141 numEdgePerFaceOut = 3; /* 3 output vertices for each face */
144 vertSamps = vertSamp4;
145 edgeFlags = edgeFlag4;
146 numEdgePerFaceOut = 6; /* 6 output vertices for each face */
149 vertSamps = vertSamp5;
150 edgeFlags = edgeFlag5;
151 numEdgePerFaceOut = 9; /* 9 output vertices for each face */
155 * Build array with vertices from vertex coordinates and vertex indices
156 * Do same for normals.
157 * Need to do this because of different normals at shared vertices
158 * (and because normals' coordinates need to be negated).
160 for (i=0; i<numFaces; i++)
163 int faceIdxVertIdx = i*numEdgePerFaceIn; // index to first element of "row" in vertex indices
164 for (j=0; j<numEdgePerFaceOut; j++)
166 int outIdx = i*numEdgePerFaceOut*3+j*3;
167 int vertIdx = vertIndices[faceIdxVertIdx+vertSamps[j]]*3;
169 vertOut[outIdx ] = vertices[vertIdx ];
170 vertOut[outIdx+1] = vertices[vertIdx+1];
171 vertOut[outIdx+2] = vertices[vertIdx+2];
173 normOut[outIdx ] = normals [normIdx ];
174 normOut[outIdx+1] = normals [normIdx+1];
175 normOut[outIdx+2] = normals [normIdx+2];
178 edgeFlagsOut[i*numEdgePerFaceOut+j] = edgeFlags[j];
183 static void fghGenerateGeometry(int numFaces, int numEdgePerFace, GLdouble *vertices, GLubyte *vertIndices, GLdouble *normals, GLdouble *vertOut, GLdouble *normOut)
185 fghGenerateGeometryWithEdgeFlag(numFaces, numEdgePerFace, vertices, vertIndices, normals, vertOut, normOut, NULL);
189 /* -- INTERNAL SETUP OF GEOMETRY --------------------------------------- */
190 static unsigned int ipow (int x, unsigned int y)
192 return y==0? 1: y==1? x: (y%2? x: 1) * ipow(x*x, y/2);
195 /* -- stuff that can be cached -- */
196 /* Cache of input to glDrawArrays */
197 #define DECLARE_SHAPE_CACHE(name,nameICaps,nameCaps)\
198 static GLboolean name##Cached = FALSE;\
199 static GLdouble name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
200 static GLdouble name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
201 static void fgh##nameICaps##Generate()\
203 fghGenerateGeometry(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
204 name##_v, name##_vi, name##_n,\
205 name##_verts, name##_norms);\
207 #define DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(name,nameICaps,nameCaps)\
208 static GLboolean name##Cached = FALSE;\
209 static GLdouble name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
210 static GLdouble name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
211 static GLboolean name##_edgeFlags[nameCaps##_VERT_PER_OBJ_TRI];\
212 static void fgh##nameICaps##Generate()\
214 fghGenerateGeometryWithEdgeFlag(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
215 name##_v, name##_vi, name##_n,\
216 name##_verts, name##_norms, name##_edgeFlags);\
219 * In general, we build arrays with all vertices or normals.
220 * We cant compress this and use glDrawElements as all combinations of
221 * vertex and normals are unique.
225 #define CUBE_NUM_VERT 8
226 #define CUBE_NUM_FACES 6
227 #define CUBE_NUM_EDGE_PER_FACE 4
228 #define CUBE_VERT_PER_OBJ (CUBE_NUM_FACES*CUBE_NUM_EDGE_PER_FACE)
229 #define CUBE_VERT_PER_OBJ_TRI (CUBE_VERT_PER_OBJ+CUBE_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
230 #define CUBE_VERT_ELEM_PER_OBJ (CUBE_VERT_PER_OBJ_TRI*3)
231 /* Vertex Coordinates */
232 static GLdouble cube_v[CUBE_NUM_VERT*3] =
244 static GLdouble cube_n[CUBE_NUM_FACES*3] =
255 static GLubyte cube_vi[CUBE_VERT_PER_OBJ] =
264 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(cube,Cube,CUBE);
266 /* -- Dodecahedron -- */
267 /* Magic Numbers: It is possible to create a dodecahedron by attaching two
268 * pentagons to each face of of a cube. The coordinates of the points are:
269 * (+-x,0, z); (+-1, 1, 1); (0, z, x )
270 * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
271 * x = 0.61803398875 and z = 1.61803398875.
273 #define DODECAHEDRON_NUM_VERT 20
274 #define DODECAHEDRON_NUM_FACES 12
275 #define DODECAHEDRON_NUM_EDGE_PER_FACE 5
276 #define DODECAHEDRON_VERT_PER_OBJ (DODECAHEDRON_NUM_FACES*DODECAHEDRON_NUM_EDGE_PER_FACE)
277 #define DODECAHEDRON_VERT_PER_OBJ_TRI (DODECAHEDRON_VERT_PER_OBJ+DODECAHEDRON_NUM_FACES*4) /* 4 extra edges per face when drawing pentagons as triangles */
278 #define DODECAHEDRON_VERT_ELEM_PER_OBJ (DODECAHEDRON_VERT_PER_OBJ_TRI*3)
279 /* Vertex Coordinates */
280 static GLdouble dodecahedron_v[DODECAHEDRON_NUM_VERT*3] =
282 0.0 , 1.61803398875, 0.61803398875,
284 -0.61803398875, 0.0 , 1.61803398875,
285 0.61803398875, 0.0 , 1.61803398875,
287 0.0 , 1.61803398875, -0.61803398875,
289 0.61803398875, 0.0 , -1.61803398875,
290 -0.61803398875, 0.0 , -1.61803398875,
292 0.0 , -1.61803398875, 0.61803398875,
295 0.0 , -1.61803398875, -0.61803398875,
298 1.61803398875, -0.61803398875, 0.0 ,
299 1.61803398875, 0.61803398875, 0.0 ,
300 -1.61803398875, 0.61803398875, 0.0 ,
301 -1.61803398875, -0.61803398875, 0.0
304 static GLdouble dodecahedron_n[DODECAHEDRON_NUM_FACES*3] =
306 0.0 , 0.525731112119, 0.850650808354,
307 0.0 , 0.525731112119, -0.850650808354,
308 0.0 , -0.525731112119, 0.850650808354,
309 0.0 , -0.525731112119, -0.850650808354,
311 0.850650808354, 0.0 , 0.525731112119,
312 -0.850650808354, 0.0 , 0.525731112119,
313 0.850650808354, 0.0 , -0.525731112119,
314 -0.850650808354, 0.0 , -0.525731112119,
316 0.525731112119, 0.850650808354, 0.0 ,
317 0.525731112119, -0.850650808354, 0.0 ,
318 -0.525731112119, 0.850650808354, 0.0 ,
319 -0.525731112119, -0.850650808354, 0.0 ,
323 static GLubyte dodecahedron_vi[DODECAHEDRON_VERT_PER_OBJ] =
340 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON);
343 /* -- Icosahedron -- */
344 #define ICOSAHEDRON_NUM_VERT 12
345 #define ICOSAHEDRON_NUM_FACES 20
346 #define ICOSAHEDRON_NUM_EDGE_PER_FACE 3
347 #define ICOSAHEDRON_VERT_PER_OBJ (ICOSAHEDRON_NUM_FACES*ICOSAHEDRON_NUM_EDGE_PER_FACE)
348 #define ICOSAHEDRON_VERT_PER_OBJ_TRI ICOSAHEDRON_VERT_PER_OBJ
349 #define ICOSAHEDRON_VERT_ELEM_PER_OBJ (ICOSAHEDRON_VERT_PER_OBJ_TRI*3)
350 /* Vertex Coordinates */
351 static GLdouble icosahedron_v[ICOSAHEDRON_NUM_VERT*3] =
354 0.447213595500, 0.894427191000, 0.0 ,
355 0.447213595500, 0.276393202252, 0.850650808354,
356 0.447213595500, -0.723606797748, 0.525731112119,
357 0.447213595500, -0.723606797748, -0.525731112119,
358 0.447213595500, 0.276393202252, -0.850650808354,
359 -0.447213595500, -0.894427191000, 0.0 ,
360 -0.447213595500, -0.276393202252, 0.850650808354,
361 -0.447213595500, 0.723606797748, 0.525731112119,
362 -0.447213595500, 0.723606797748, -0.525731112119,
363 -0.447213595500, -0.276393202252, -0.850650808354,
367 * icosahedron_n[i][0] = ( icosahedron_v[icosahedron_vi[i][1]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) * ( icosahedron_v[icosahedron_vi[i][2]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) - ( icosahedron_v[icosahedron_vi[i][1]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) * ( icosahedron_v[icosahedron_vi[i][2]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) ;
368 * icosahedron_n[i][1] = ( icosahedron_v[icosahedron_vi[i][1]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) * ( icosahedron_v[icosahedron_vi[i][2]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) - ( icosahedron_v[icosahedron_vi[i][1]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) * ( icosahedron_v[icosahedron_vi[i][2]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) ;
369 * icosahedron_n[i][2] = ( icosahedron_v[icosahedron_vi[i][1]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) * ( icosahedron_v[icosahedron_vi[i][2]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) - ( icosahedron_v[icosahedron_vi[i][1]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) * ( icosahedron_v[icosahedron_vi[i][2]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) ;
371 static GLdouble icosahedron_n[ICOSAHEDRON_NUM_FACES*3] =
373 0.760845213037948, 0.470228201835026, 0.341640786498800,
374 0.760845213036861, -0.179611190632978, 0.552786404500000,
375 0.760845213033849, -0.581234022404097, 0,
376 0.760845213036861, -0.179611190632978, -0.552786404500000,
377 0.760845213037948, 0.470228201835026, -0.341640786498800,
378 0.179611190628666, 0.760845213037948, 0.552786404498399,
379 0.179611190634277, -0.290617011204044, 0.894427191000000,
380 0.179611190633958, -0.940456403667806, 0,
381 0.179611190634278, -0.290617011204044, -0.894427191000000,
382 0.179611190628666, 0.760845213037948, -0.552786404498399,
383 -0.179611190633958, 0.940456403667806, 0,
384 -0.179611190634277, 0.290617011204044, 0.894427191000000,
385 -0.179611190628666, -0.760845213037948, 0.552786404498399,
386 -0.179611190628666, -0.760845213037948, -0.552786404498399,
387 -0.179611190634277, 0.290617011204044, -0.894427191000000,
388 -0.760845213036861, 0.179611190632978, -0.552786404500000,
389 -0.760845213033849, 0.581234022404097, 0,
390 -0.760845213036861, 0.179611190632978, 0.552786404500000,
391 -0.760845213037948, -0.470228201835026, 0.341640786498800,
392 -0.760845213037948, -0.470228201835026, -0.341640786498800,
396 static GLubyte icosahedron_vi[ICOSAHEDRON_VERT_PER_OBJ] =
419 DECLARE_SHAPE_CACHE(icosahedron,Icosahedron,ICOSAHEDRON);
421 /* -- Octahedron -- */
422 #define OCTAHEDRON_NUM_VERT 6
423 #define OCTAHEDRON_NUM_FACES 8
424 #define OCTAHEDRON_NUM_EDGE_PER_FACE 3
425 #define OCTAHEDRON_VERT_PER_OBJ (OCTAHEDRON_NUM_FACES*OCTAHEDRON_NUM_EDGE_PER_FACE)
426 #define OCTAHEDRON_VERT_PER_OBJ_TRI OCTAHEDRON_VERT_PER_OBJ
427 #define OCTAHEDRON_VERT_ELEM_PER_OBJ (OCTAHEDRON_VERT_PER_OBJ_TRI*3)
429 /* Vertex Coordinates */
430 static GLdouble octahedron_v[OCTAHEDRON_NUM_VERT*3] =
441 static GLdouble octahedron_n[OCTAHEDRON_NUM_FACES*3] =
443 0.577350269189, 0.577350269189, 0.577350269189, /* sqrt(1/3) */
444 0.577350269189, 0.577350269189,-0.577350269189,
445 0.577350269189,-0.577350269189, 0.577350269189,
446 0.577350269189,-0.577350269189,-0.577350269189,
447 -0.577350269189, 0.577350269189, 0.577350269189,
448 -0.577350269189, 0.577350269189,-0.577350269189,
449 -0.577350269189,-0.577350269189, 0.577350269189,
450 -0.577350269189,-0.577350269189,-0.577350269189
455 static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OBJ] =
466 DECLARE_SHAPE_CACHE(octahedron,Octahedron,OCTAHEDRON);
468 /* -- RhombicDodecahedron -- */
469 #define RHOMBICDODECAHEDRON_NUM_VERT 14
470 #define RHOMBICDODECAHEDRON_NUM_FACES 12
471 #define RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE 4
472 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ (RHOMBICDODECAHEDRON_NUM_FACES*RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE)
473 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ_TRI (RHOMBICDODECAHEDRON_VERT_PER_OBJ+RHOMBICDODECAHEDRON_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
474 #define RHOMBICDODECAHEDRON_VERT_ELEM_PER_OBJ (RHOMBICDODECAHEDRON_VERT_PER_OBJ_TRI*3)
476 /* Vertex Coordinates */
477 static GLdouble rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] =
480 0.707106781187, 0.0 , 0.5,
481 0.0 , 0.707106781187, 0.5,
482 -0.707106781187, 0.0 , 0.5,
483 0.0 , -0.707106781187, 0.5,
484 0.707106781187, 0.707106781187, 0.0,
485 -0.707106781187, 0.707106781187, 0.0,
486 -0.707106781187, -0.707106781187, 0.0,
487 0.707106781187, -0.707106781187, 0.0,
488 0.707106781187, 0.0 , -0.5,
489 0.0 , 0.707106781187, -0.5,
490 -0.707106781187, 0.0 , -0.5,
491 0.0 , -0.707106781187, -0.5,
495 static GLdouble rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] =
497 0.353553390594, 0.353553390594, 0.5,
498 -0.353553390594, 0.353553390594, 0.5,
499 -0.353553390594, -0.353553390594, 0.5,
500 0.353553390594, -0.353553390594, 0.5,
505 0.353553390594, 0.353553390594, -0.5,
506 -0.353553390594, 0.353553390594, -0.5,
507 -0.353553390594, -0.353553390594, -0.5,
508 0.353553390594, -0.353553390594, -0.5
512 static GLubyte rhombicdodecahedron_vi[RHOMBICDODECAHEDRON_VERT_PER_OBJ] =
527 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON);
529 /* -- Tetrahedron -- */
530 /* Magic Numbers: r0 = ( 1, 0, 0 )
531 * r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
532 * r2 = ( -1/3, - sqrt(2) / 3, sqrt(6) / 3 )
533 * r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
534 * |r0| = |r1| = |r2| = |r3| = 1
535 * Distance between any two points is 2 sqrt(6) / 3
537 * Normals: The unit normals are simply the negative of the coordinates of the point not on the surface.
539 #define TETRAHEDRON_NUM_VERT 4
540 #define TETRAHEDRON_NUM_FACES 4
541 #define TETRAHEDRON_NUM_EDGE_PER_FACE 3
542 #define TETRAHEDRON_VERT_PER_OBJ (TETRAHEDRON_NUM_FACES*TETRAHEDRON_NUM_EDGE_PER_FACE)
543 #define TETRAHEDRON_VERT_PER_OBJ_TRI TETRAHEDRON_VERT_PER_OBJ
544 #define TETRAHEDRON_VERT_ELEM_PER_OBJ (TETRAHEDRON_VERT_PER_OBJ_TRI*3)
546 /* Vertex Coordinates */
547 static GLdouble tetrahedron_v[TETRAHEDRON_NUM_VERT*3] =
550 -0.333333333333, 0.942809041582, 0.0,
551 -0.333333333333, -0.471404520791, 0.816496580928,
552 -0.333333333333, -0.471404520791, -0.816496580928
555 static GLdouble tetrahedron_n[TETRAHEDRON_NUM_FACES*3] =
558 0.333333333333, -0.942809041582, 0.0,
559 0.333333333333, 0.471404520791, -0.816496580928,
560 0.333333333333, 0.471404520791, 0.816496580928
564 static GLubyte tetrahedron_vi[TETRAHEDRON_VERT_PER_OBJ] =
571 DECLARE_SHAPE_CACHE(tetrahedron,Tetrahedron,TETRAHEDRON);
573 /* -- Sierpinski Sponge -- */
574 static void fghSierpinskiSpongeGenerate ( int numLevels, GLdouble offset[3], GLdouble scale, GLdouble* vertices, GLdouble* normals )
577 if ( numLevels == 0 )
579 for (i=0; i<TETRAHEDRON_NUM_FACES; i++)
582 int faceIdxVertIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE;
583 for (j=0; j<TETRAHEDRON_NUM_EDGE_PER_FACE; j++)
585 int outIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE*3+j*3;
586 int vertIdx = tetrahedron_vi[faceIdxVertIdx+j]*3;
588 vertices[outIdx ] = offset[0] + scale * tetrahedron_v[vertIdx ];
589 vertices[outIdx+1] = offset[1] + scale * tetrahedron_v[vertIdx+1];
590 vertices[outIdx+2] = offset[2] + scale * tetrahedron_v[vertIdx+2];
592 normals [outIdx ] = tetrahedron_n[normIdx ];
593 normals [outIdx+1] = tetrahedron_n[normIdx+1];
594 normals [outIdx+2] = tetrahedron_n[normIdx+2];
598 else if ( numLevels > 0 )
600 GLdouble local_offset[3] ; /* Use a local variable to avoid buildup of roundoff errors */
601 unsigned int stride = ipow(4,--numLevels)*TETRAHEDRON_VERT_ELEM_PER_OBJ;
603 for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ )
606 local_offset[0] = offset[0] + scale * tetrahedron_v[idx ];
607 local_offset[1] = offset[1] + scale * tetrahedron_v[idx+1];
608 local_offset[2] = offset[2] + scale * tetrahedron_v[idx+2];
609 fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride );
614 /* -- Now the various shapes involving circles -- */
616 * Compute lookup table of cos and sin values forming a cirle
619 * It is the responsibility of the caller to free these tables
620 * The size of the table is (n+1) to form a connected loop
621 * The last entry is exactly the same as the first
622 * The sign of n can be flipped to get the reverse loop
624 static void fghCircleTable(double **sint,double **cost,const int n)
628 /* Table size, the sign of n flips the circle direction */
630 const int size = abs(n);
632 /* Determine the angle between samples */
634 const double angle = 2*M_PI/(double)( ( n == 0 ) ? 1 : n );
636 /* Allocate memory for n samples, plus duplicate of first entry at the end */
638 *sint = (double *) calloc(sizeof(double), size+1);
639 *cost = (double *) calloc(sizeof(double), size+1);
641 /* Bail out if memory allocation fails, fgError never returns */
643 if (!(*sint) || !(*cost))
647 fgError("Failed to allocate memory in fghCircleTable");
650 /* Compute cos and sin around the circle */
655 for (i=1; i<size; i++)
657 (*sint)[i] = sin(angle*i);
658 (*cost)[i] = cos(angle*i);
661 /* Last sample is duplicate of the first */
663 (*sint)[size] = (*sint)[0];
664 (*cost)[size] = (*cost)[0];
668 /* -- INTERNAL DRAWING functions --------------------------------------- */
669 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,edgeFlags)\
670 static void fgh##nameICaps( GLboolean useWireMode )\
674 fgh##nameICaps##Generate();\
675 name##Cached = GL_TRUE;\
677 fghDrawGeometry(GL_TRIANGLES,name##_verts,name##_norms,edgeFlags,nameCaps##_VERT_PER_OBJ_TRI,useWireMode);\
679 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
680 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_edgeFlags)
682 static void fghCube( GLdouble dSize, GLboolean useWireMode )
687 cubeCached = GL_TRUE;
694 /* Need to build new vertex list containing vertices for cube of different size */
695 GLdouble *vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLdouble));
696 for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
697 vertices[i] = dSize*cube_verts[i];
699 fghDrawGeometry(GL_TRIANGLES,vertices ,cube_norms,cube_edgeFlags,CUBE_VERT_PER_OBJ_TRI,useWireMode);
702 fghDrawGeometry(GL_TRIANGLES,cube_verts,cube_norms,cube_edgeFlags,CUBE_VERT_PER_OBJ_TRI,useWireMode);
705 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON);
706 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON);
707 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON);
708 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON);
709 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON);
711 static void fghSierpinskiSponge ( int numLevels, GLdouble offset[3], GLdouble scale, GLboolean useWireMode )
715 GLsizei numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
716 GLsizei numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
720 /* Allocate memory */
721 vertices = malloc(numVert*3 * sizeof(GLdouble));
722 normals = malloc(numVert*3 * sizeof(GLdouble));
724 /* Generate elements */
725 fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
727 /* Draw and cleanup */
728 fghDrawGeometry(GL_TRIANGLES,vertices,normals,NULL,numVert,useWireMode);
735 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
739 * Draws a solid sphere
741 void FGAPIENTRY glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
745 /* Adjust z and radius as stacks are drawn. */
750 /* Pre-computed circle */
752 double *sint1,*cost1;
753 double *sint2,*cost2;
755 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
757 fghCircleTable(&sint1,&cost1,-slices);
758 fghCircleTable(&sint2,&cost2,stacks*2);
760 /* The top stack is covered with a triangle fan */
763 z1 = cost2[(stacks>0)?1:0];
765 r1 = sint2[(stacks>0)?1:0];
767 glBegin(GL_TRIANGLE_FAN);
770 glVertex3d(0,0,radius);
772 for (j=slices; j>=0; j--)
774 glNormal3d(cost1[j]*r1, sint1[j]*r1, z1 );
775 glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
780 /* Cover each stack with a quad strip, except the top and bottom stacks */
782 for( i=1; i<stacks-1; i++ )
784 z0 = z1; z1 = cost2[i+1];
785 r0 = r1; r1 = sint2[i+1];
787 glBegin(GL_QUAD_STRIP);
789 for(j=0; j<=slices; j++)
791 glNormal3d(cost1[j]*r1, sint1[j]*r1, z1 );
792 glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
793 glNormal3d(cost1[j]*r0, sint1[j]*r0, z0 );
794 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
800 /* The bottom stack is covered with a triangle fan */
805 glBegin(GL_TRIANGLE_FAN);
808 glVertex3d(0,0,-radius);
810 for (j=0; j<=slices; j++)
812 glNormal3d(cost1[j]*r0, sint1[j]*r0, z0 );
813 glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
818 /* Release sin and cos tables */
827 * Draws a wire sphere
829 void FGAPIENTRY glutWireSphere(GLdouble radius, GLint slices, GLint stacks)
833 /* Adjust z and radius as stacks and slices are drawn. */
838 /* Pre-computed circle */
840 double *sint1,*cost1;
841 double *sint2,*cost2;
843 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
845 fghCircleTable(&sint1,&cost1,-slices );
846 fghCircleTable(&sint2,&cost2, stacks*2);
848 /* Draw a line loop for each stack */
850 for (i=1; i<stacks; i++)
855 glBegin(GL_LINE_LOOP);
857 for(j=0; j<=slices; j++)
863 glVertex3d(x*r*radius,y*r*radius,z*radius);
869 /* Draw a line loop for each slice */
871 for (i=0; i<slices; i++)
873 glBegin(GL_LINE_STRIP);
875 for(j=0; j<=stacks; j++)
877 x = cost1[i]*sint2[j];
878 y = sint1[i]*sint2[j];
882 glVertex3d(x*radius,y*radius,z*radius);
888 /* Release sin and cos tables */
899 void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks )
903 /* Step in z and radius as stacks are drawn. */
908 const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
909 const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
911 /* Scaling factors for vertex normals */
913 const double cosn = ( height / sqrt ( height * height + base * base ));
914 const double sinn = ( base / sqrt ( height * height + base * base ));
916 /* Pre-computed circle */
920 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
922 fghCircleTable(&sint,&cost,-slices);
924 /* Cover the circular base with a triangle fan... */
932 glBegin(GL_TRIANGLE_FAN);
934 glNormal3d(0.0,0.0,-1.0);
935 glVertex3d(0.0,0.0, z0 );
937 for (j=0; j<=slices; j++)
938 glVertex3d(cost[j]*r0, sint[j]*r0, z0);
942 /* Cover each stack with a quad strip, except the top stack */
944 for( i=0; i<stacks-1; i++ )
946 glBegin(GL_QUAD_STRIP);
948 for(j=0; j<=slices; j++)
950 glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
951 glVertex3d(cost[j]*r0, sint[j]*r0, z0 );
952 glVertex3d(cost[j]*r1, sint[j]*r1, z1 );
955 z0 = z1; z1 += zStep;
956 r0 = r1; r1 -= rStep;
961 /* The top stack is covered with individual triangles */
963 glBegin(GL_TRIANGLES);
965 glNormal3d(cost[0]*sinn, sint[0]*sinn, cosn);
967 for (j=0; j<slices; j++)
969 glVertex3d(cost[j+0]*r0, sint[j+0]*r0, z0 );
970 glVertex3d(0, 0, height);
971 glNormal3d(cost[j+1]*sinn, sint[j+1]*sinn, cosn );
972 glVertex3d(cost[j+1]*r0, sint[j+1]*r0, z0 );
977 /* Release sin and cos tables */
986 void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks)
990 /* Step in z and radius as stacks are drawn. */
995 const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
996 const double rStep = base / ( ( stacks > 0 ) ? stacks : 1 );
998 /* Scaling factors for vertex normals */
1000 const double cosn = ( height / sqrt ( height * height + base * base ));
1001 const double sinn = ( base / sqrt ( height * height + base * base ));
1003 /* Pre-computed circle */
1007 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1009 fghCircleTable(&sint,&cost,-slices);
1011 /* Draw the stacks... */
1013 for (i=0; i<stacks; i++)
1015 glBegin(GL_LINE_LOOP);
1017 for( j=0; j<slices; j++ )
1019 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn);
1020 glVertex3d(cost[j]*r, sint[j]*r, z );
1029 /* Draw the slices */
1035 for (j=0; j<slices; j++)
1037 glNormal3d(cost[j]*sinn, sint[j]*sinn, cosn );
1038 glVertex3d(cost[j]*r, sint[j]*r, 0.0 );
1039 glVertex3d(0.0, 0.0, height);
1044 /* Release sin and cos tables */
1052 * Draws a solid cylinder
1054 void FGAPIENTRY glutSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1058 /* Step in z and radius as stacks are drawn. */
1061 const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1063 /* Pre-computed circle */
1067 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1069 fghCircleTable(&sint,&cost,-slices);
1071 /* Cover the base and top */
1073 glBegin(GL_TRIANGLE_FAN);
1074 glNormal3d(0.0, 0.0, -1.0 );
1075 glVertex3d(0.0, 0.0, 0.0 );
1076 for (j=0; j<=slices; j++)
1077 glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
1080 glBegin(GL_TRIANGLE_FAN);
1081 glNormal3d(0.0, 0.0, 1.0 );
1082 glVertex3d(0.0, 0.0, height);
1083 for (j=slices; j>=0; j--)
1084 glVertex3d(cost[j]*radius, sint[j]*radius, height);
1092 for (i=1; i<=stacks; i++)
1097 glBegin(GL_QUAD_STRIP);
1098 for (j=0; j<=slices; j++ )
1100 glNormal3d(cost[j], sint[j], 0.0 );
1101 glVertex3d(cost[j]*radius, sint[j]*radius, z0 );
1102 glVertex3d(cost[j]*radius, sint[j]*radius, z1 );
1106 z0 = z1; z1 += zStep;
1109 /* Release sin and cos tables */
1116 * Draws a wire cylinder
1118 void FGAPIENTRY glutWireCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
1122 /* Step in z and radius as stacks are drawn. */
1125 const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );
1127 /* Pre-computed circle */
1131 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1133 fghCircleTable(&sint,&cost,-slices);
1135 /* Draw the stacks... */
1137 for (i=0; i<=stacks; i++)
1142 glBegin(GL_LINE_LOOP);
1144 for( j=0; j<slices; j++ )
1146 glNormal3d(cost[j], sint[j], 0.0);
1147 glVertex3d(cost[j]*radius, sint[j]*radius, z );
1155 /* Draw the slices */
1159 for (j=0; j<slices; j++)
1161 glNormal3d(cost[j], sint[j], 0.0 );
1162 glVertex3d(cost[j]*radius, sint[j]*radius, 0.0 );
1163 glVertex3d(cost[j]*radius, sint[j]*radius, height);
1168 /* Release sin and cos tables */
1175 * Draws a wire torus
1177 void FGAPIENTRY glutWireTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1179 double iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1180 double *vertex, *normal;
1182 double spsi, cpsi, sphi, cphi ;
1184 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1186 if ( nSides < 1 ) nSides = 1;
1187 if ( nRings < 1 ) nRings = 1;
1189 /* Allocate the vertices array */
1190 vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1191 normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1195 dpsi = 2.0 * M_PI / (double)nRings ;
1196 dphi = -2.0 * M_PI / (double)nSides ;
1199 for( j=0; j<nRings; j++ )
1201 cpsi = cos ( psi ) ;
1202 spsi = sin ( psi ) ;
1205 for( i=0; i<nSides; i++ )
1207 int offset = 3 * ( j * nSides + i ) ;
1208 cphi = cos ( phi ) ;
1209 sphi = sin ( phi ) ;
1210 *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1211 *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1212 *(vertex + offset + 2) = sphi * iradius ;
1213 *(normal + offset + 0) = cpsi * cphi ;
1214 *(normal + offset + 1) = spsi * cphi ;
1215 *(normal + offset + 2) = sphi ;
1222 for( i=0; i<nSides; i++ )
1224 glBegin( GL_LINE_LOOP );
1226 for( j=0; j<nRings; j++ )
1228 int offset = 3 * ( j * nSides + i ) ;
1229 glNormal3dv( normal + offset );
1230 glVertex3dv( vertex + offset );
1236 for( j=0; j<nRings; j++ )
1238 glBegin(GL_LINE_LOOP);
1240 for( i=0; i<nSides; i++ )
1242 int offset = 3 * ( j * nSides + i ) ;
1243 glNormal3dv( normal + offset );
1244 glVertex3dv( vertex + offset );
1256 * Draws a solid torus
1258 void FGAPIENTRY glutSolidTorus( GLdouble dInnerRadius, GLdouble dOuterRadius, GLint nSides, GLint nRings )
1260 double iradius = dInnerRadius, oradius = dOuterRadius, phi, psi, dpsi, dphi;
1261 double *vertex, *normal;
1263 double spsi, cpsi, sphi, cphi ;
1265 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1267 if ( nSides < 1 ) nSides = 1;
1268 if ( nRings < 1 ) nRings = 1;
1270 /* Increment the number of sides and rings to allow for one more point than surface */
1274 /* Allocate the vertices array */
1275 vertex = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1276 normal = (double *)calloc( sizeof(double), 3 * nSides * nRings );
1280 dpsi = 2.0 * M_PI / (double)(nRings - 1) ;
1281 dphi = -2.0 * M_PI / (double)(nSides - 1) ;
1284 for( j=0; j<nRings; j++ )
1286 cpsi = cos ( psi ) ;
1287 spsi = sin ( psi ) ;
1290 for( i=0; i<nSides; i++ )
1292 int offset = 3 * ( j * nSides + i ) ;
1293 cphi = cos ( phi ) ;
1294 sphi = sin ( phi ) ;
1295 *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1296 *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1297 *(vertex + offset + 2) = sphi * iradius ;
1298 *(normal + offset + 0) = cpsi * cphi ;
1299 *(normal + offset + 1) = spsi * cphi ;
1300 *(normal + offset + 2) = sphi ;
1307 glBegin( GL_QUADS );
1308 for( i=0; i<nSides-1; i++ )
1310 for( j=0; j<nRings-1; j++ )
1312 int offset = 3 * ( j * nSides + i ) ;
1313 glNormal3dv( normal + offset );
1314 glVertex3dv( vertex + offset );
1315 glNormal3dv( normal + offset + 3 );
1316 glVertex3dv( vertex + offset + 3 );
1317 glNormal3dv( normal + offset + 3 * nSides + 3 );
1318 glVertex3dv( vertex + offset + 3 * nSides + 3 );
1319 glNormal3dv( normal + offset + 3 * nSides );
1320 glVertex3dv( vertex + offset + 3 * nSides );
1333 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1334 /* Macro to generate interface functions */
1335 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1336 void FGAPIENTRY glutWire##nameICaps( void )\
1338 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1339 fgh##nameICaps( TRUE );\
1341 void FGAPIENTRY glutSolid##nameICaps( void )\
1343 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1344 fgh##nameICaps( FALSE );\
1347 void FGAPIENTRY glutWireCube( GLdouble dSize )
1349 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1350 fghCube( dSize, TRUE );
1352 void FGAPIENTRY glutSolidCube( GLdouble dSize )
1354 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1355 fghCube( dSize, FALSE );
1358 DECLARE_SHAPE_INTERFACE(Dodecahedron);
1359 DECLARE_SHAPE_INTERFACE(Icosahedron);
1360 DECLARE_SHAPE_INTERFACE(Octahedron);
1361 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron);
1363 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1365 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1366 fghSierpinskiSponge ( num_levels, offset, scale, TRUE );
1368 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale )
1370 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1371 fghSierpinskiSponge ( num_levels, offset, scale, FALSE );
1374 DECLARE_SHAPE_INTERFACE(Tetrahedron);
1377 /*** END OF FILE ***/