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"
32 * Need more types of polyhedra? See CPolyhedron in MRPT
36 #ifndef GL_ES_VERSION_2_0
37 /* General functions for drawing geometry
38 * Solids are drawn by glDrawArrays if composed of triangles, or by
39 * glDrawElements if consisting of squares or pentagons that were
40 * decomposed into triangles (some vertices are repeated in that case).
41 * WireFrame drawing will have to be done per face, using GL_LINE_LOOP and
42 * issuing one draw call per face. Always use glDrawArrays as no triangle
43 * decomposition needed. We use the "first" parameter in glDrawArrays to go
46 static void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numFaces, GLsizei numEdgePerFace)
50 glEnableClientState(GL_VERTEX_ARRAY);
51 glEnableClientState(GL_NORMAL_ARRAY);
53 glVertexPointer(3, GL_FLOAT, 0, vertices);
54 glNormalPointer(GL_FLOAT, 0, normals);
56 /* Draw per face (TODO: could use glMultiDrawArrays if available) */
57 for (i=0; i<numFaces; i++)
58 glDrawArrays(GL_LINE_LOOP, i*numEdgePerFace, numEdgePerFace);
60 glDisableClientState(GL_VERTEX_ARRAY);
61 glDisableClientState(GL_NORMAL_ARRAY);
64 * Draw the geometric shape with filled triangles
66 * - If the shape is naturally triangulated (numEdgePerFace==3), each
67 * vertex+normal pair is used only once, so no vertex indices.
69 * - If the shape was triangulated (DECOMPOSE_TO_TRIANGLE), some
70 * vertex+normal pairs are reused, so use vertex indices.
72 static void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLubyte *vertIdxs,
73 GLsizei numVertices, GLsizei numVertIdxs)
75 glEnableClientState(GL_VERTEX_ARRAY);
76 glEnableClientState(GL_NORMAL_ARRAY);
78 glVertexPointer(3, GL_FLOAT, 0, vertices);
79 glNormalPointer(GL_FLOAT, 0, normals);
81 glDrawArrays(GL_TRIANGLES, 0, numVertices);
83 glDrawElements(GL_TRIANGLES, numVertIdxs, GL_UNSIGNED_BYTE, vertIdxs);
85 glDisableClientState(GL_VERTEX_ARRAY);
86 glDisableClientState(GL_NORMAL_ARRAY);
89 /* Shape decomposition to triangles
90 * We'll use glDrawElements to draw all shapes that are not naturally
91 * composed of triangles, so generate an index vector here, using the
92 * below sampling scheme.
93 * Be careful to keep winding of all triangles counter-clockwise,
94 * assuming that input has correct winding...
96 static GLubyte vert4Decomp[6] = {0,1,2, 0,2,3}; /* quad : 4 input vertices, 6 output (2 triangles) */
97 static GLubyte vert5Decomp[9] = {0,1,2, 0,2,4, 4,2,3}; /* pentagon: 5 input vertices, 9 output (3 triangles) */
99 static void fghGenerateGeometryWithIndexArray(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut, GLubyte *vertIdxOut)
101 int i,j,numEdgeIdxPerFace;
102 GLubyte *vertSamps = NULL;
103 switch (numEdgePerFace)
106 /* nothing to do here, we'll draw with glDrawArrays */
109 vertSamps = vert4Decomp;
110 numEdgeIdxPerFace = 6; /* 6 output vertices for each face */
113 vertSamps = vert5Decomp;
114 numEdgeIdxPerFace = 9; /* 9 output vertices for each face */
118 * Build array with vertices using vertex coordinates and vertex indices
119 * Do same for normals.
120 * Need to do this because of different normals at shared vertices.
122 for (i=0; i<numFaces; i++)
125 int faceIdxVertIdx = i*numEdgePerFace; // index to first element of "row" in vertex indices
126 for (j=0; j<numEdgePerFace; j++)
128 int outIdx = i*numEdgePerFace*3+j*3;
129 int vertIdx = vertIndices[faceIdxVertIdx+j]*3;
131 vertOut[outIdx ] = vertices[vertIdx ];
132 vertOut[outIdx+1] = vertices[vertIdx+1];
133 vertOut[outIdx+2] = vertices[vertIdx+2];
135 normOut[outIdx ] = normals [normIdx ];
136 normOut[outIdx+1] = normals [normIdx+1];
137 normOut[outIdx+2] = normals [normIdx+2];
140 /* generate vertex indices for each face */
142 for (j=0; j<numEdgeIdxPerFace; j++)
143 vertIdxOut[i*numEdgeIdxPerFace+j] = faceIdxVertIdx + vertSamps[j];
147 static void fghGenerateGeometry(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut)
149 /* This function does the same as fghGenerateGeometryWithIndexArray, just skipping the index array generation... */
150 fghGenerateGeometryWithIndexArray(numFaces, numEdgePerFace, vertices, vertIndices, normals, vertOut, normOut, NULL);
154 /* -- INTERNAL SETUP OF GEOMETRY --------------------------------------- */
155 /* -- stuff that can be cached -- */
156 /* Cache of input to glDrawArrays or glDrawElements
157 * In general, we build arrays with all vertices or normals.
158 * We cant compress this and use glDrawElements as all combinations of
159 * vertices and normals are unique.
161 #define DECLARE_SHAPE_CACHE(name,nameICaps,nameCaps)\
162 static GLboolean name##Cached = FALSE;\
163 static GLfloat name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
164 static GLfloat name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
165 static void fgh##nameICaps##Generate()\
167 fghGenerateGeometry(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
168 name##_v, name##_vi, name##_n,\
169 name##_verts, name##_norms);\
171 #define DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(name,nameICaps,nameCaps)\
172 static GLboolean name##Cached = FALSE;\
173 static GLfloat name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
174 static GLfloat name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
175 static GLubyte name##_vertIdxs[nameCaps##_VERT_PER_OBJ_TRI];\
176 static void fgh##nameICaps##Generate()\
178 fghGenerateGeometryWithIndexArray(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
179 name##_v, name##_vi, name##_n,\
180 name##_verts, name##_norms, name##_vertIdxs);\
184 #define CUBE_NUM_VERT 8
185 #define CUBE_NUM_FACES 6
186 #define CUBE_NUM_EDGE_PER_FACE 4
187 #define CUBE_VERT_PER_OBJ (CUBE_NUM_FACES*CUBE_NUM_EDGE_PER_FACE)
188 #define CUBE_VERT_ELEM_PER_OBJ (CUBE_VERT_PER_OBJ*3)
189 #define CUBE_VERT_PER_OBJ_TRI (CUBE_VERT_PER_OBJ+CUBE_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
190 /* Vertex Coordinates */
191 static GLfloat cube_v[CUBE_NUM_VERT*3] =
203 static GLfloat cube_n[CUBE_NUM_FACES*3] =
213 /* Vertex indices, as quads, before triangulation */
214 static GLubyte cube_vi[CUBE_VERT_PER_OBJ] =
223 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(cube,Cube,CUBE);
225 /* -- Dodecahedron -- */
226 /* Magic Numbers: It is possible to create a dodecahedron by attaching two
227 * pentagons to each face of of a cube. The coordinates of the points are:
228 * (+-x,0, z); (+-1, 1, 1); (0, z, x )
229 * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
230 * x = 0.61803398875 and z = 1.61803398875.
232 #define DODECAHEDRON_NUM_VERT 20
233 #define DODECAHEDRON_NUM_FACES 12
234 #define DODECAHEDRON_NUM_EDGE_PER_FACE 5
235 #define DODECAHEDRON_VERT_PER_OBJ (DODECAHEDRON_NUM_FACES*DODECAHEDRON_NUM_EDGE_PER_FACE)
236 #define DODECAHEDRON_VERT_ELEM_PER_OBJ (DODECAHEDRON_VERT_PER_OBJ*3)
237 #define DODECAHEDRON_VERT_PER_OBJ_TRI (DODECAHEDRON_VERT_PER_OBJ+DODECAHEDRON_NUM_FACES*4) /* 4 extra edges per face when drawing pentagons as triangles */
238 /* Vertex Coordinates */
239 static GLfloat dodecahedron_v[DODECAHEDRON_NUM_VERT*3] =
241 0.0f, 1.61803398875f, 0.61803398875f,
243 -0.61803398875f, 0.0f, 1.61803398875f,
244 0.61803398875f, 0.0f, 1.61803398875f,
246 0.0f, 1.61803398875f, -0.61803398875f,
248 0.61803398875f, 0.0f, -1.61803398875f,
249 -0.61803398875f, 0.0f, -1.61803398875f,
250 - 1.0f, 1.0f, - 1.0f,
251 0.0f, -1.61803398875f, 0.61803398875f,
253 - 1.0f, - 1.0f, 1.0f,
254 0.0f, -1.61803398875f, -0.61803398875f,
255 - 1.0f, - 1.0f, - 1.0f,
256 1.0f, - 1.0f, - 1.0f,
257 1.61803398875f, -0.61803398875f, 0.0f,
258 1.61803398875f, 0.61803398875f, 0.0f,
259 -1.61803398875f, 0.61803398875f, 0.0f,
260 -1.61803398875f, -0.61803398875f, 0.0f
263 static GLfloat dodecahedron_n[DODECAHEDRON_NUM_FACES*3] =
265 0.0f, 0.525731112119f, 0.850650808354f,
266 0.0f, 0.525731112119f, -0.850650808354f,
267 0.0f, -0.525731112119f, 0.850650808354f,
268 0.0f, -0.525731112119f, -0.850650808354f,
270 0.850650808354f, 0.0f, 0.525731112119f,
271 -0.850650808354f, 0.0f, 0.525731112119f,
272 0.850650808354f, 0.0f, -0.525731112119f,
273 -0.850650808354f, 0.0f, -0.525731112119f,
275 0.525731112119f, 0.850650808354f, 0.0f,
276 0.525731112119f, -0.850650808354f, 0.0f,
277 -0.525731112119f, 0.850650808354f, 0.0f,
278 -0.525731112119f, -0.850650808354f, 0.0f,
282 static GLubyte dodecahedron_vi[DODECAHEDRON_VERT_PER_OBJ] =
299 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON);
302 /* -- Icosahedron -- */
303 #define ICOSAHEDRON_NUM_VERT 12
304 #define ICOSAHEDRON_NUM_FACES 20
305 #define ICOSAHEDRON_NUM_EDGE_PER_FACE 3
306 #define ICOSAHEDRON_VERT_PER_OBJ (ICOSAHEDRON_NUM_FACES*ICOSAHEDRON_NUM_EDGE_PER_FACE)
307 #define ICOSAHEDRON_VERT_ELEM_PER_OBJ (ICOSAHEDRON_VERT_PER_OBJ*3)
308 #define ICOSAHEDRON_VERT_PER_OBJ_TRI ICOSAHEDRON_VERT_PER_OBJ
309 /* Vertex Coordinates */
310 static GLfloat icosahedron_v[ICOSAHEDRON_NUM_VERT*3] =
313 0.447213595500f, 0.894427191000f, 0.0f,
314 0.447213595500f, 0.276393202252f, 0.850650808354f,
315 0.447213595500f, -0.723606797748f, 0.525731112119f,
316 0.447213595500f, -0.723606797748f, -0.525731112119f,
317 0.447213595500f, 0.276393202252f, -0.850650808354f,
318 -0.447213595500f, -0.894427191000f, 0.0f,
319 -0.447213595500f, -0.276393202252f, 0.850650808354f,
320 -0.447213595500f, 0.723606797748f, 0.525731112119f,
321 -0.447213595500f, 0.723606797748f, -0.525731112119f,
322 -0.447213595500f, -0.276393202252f, -0.850650808354f,
326 * icosahedron_n[i][0] = ( icosahedron_v[icosahedron_vi[i][1]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) * ( icosahedron_v[icosahedron_vi[i][2]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) - ( icosahedron_v[icosahedron_vi[i][1]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) * ( icosahedron_v[icosahedron_vi[i][2]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) ;
327 * icosahedron_n[i][1] = ( icosahedron_v[icosahedron_vi[i][1]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) * ( icosahedron_v[icosahedron_vi[i][2]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) - ( icosahedron_v[icosahedron_vi[i][1]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) * ( icosahedron_v[icosahedron_vi[i][2]][2] - icosahedron_v[icosahedron_vi[i][0]][2] ) ;
328 * icosahedron_n[i][2] = ( icosahedron_v[icosahedron_vi[i][1]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) * ( icosahedron_v[icosahedron_vi[i][2]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) - ( icosahedron_v[icosahedron_vi[i][1]][1] - icosahedron_v[icosahedron_vi[i][0]][1] ) * ( icosahedron_v[icosahedron_vi[i][2]][0] - icosahedron_v[icosahedron_vi[i][0]][0] ) ;
330 static GLfloat icosahedron_n[ICOSAHEDRON_NUM_FACES*3] =
332 0.760845213037948f, 0.470228201835026f, 0.341640786498800f,
333 0.760845213036861f, -0.179611190632978f, 0.552786404500000f,
334 0.760845213033849f, -0.581234022404097f, 0.0f,
335 0.760845213036861f, -0.179611190632978f, -0.552786404500000f,
336 0.760845213037948f, 0.470228201835026f, -0.341640786498800f,
337 0.179611190628666f, 0.760845213037948f, 0.552786404498399f,
338 0.179611190634277f, -0.290617011204044f, 0.894427191000000f,
339 0.179611190633958f, -0.940456403667806f, 0.0f,
340 0.179611190634278f, -0.290617011204044f, -0.894427191000000f,
341 0.179611190628666f, 0.760845213037948f, -0.552786404498399f,
342 -0.179611190633958f, 0.940456403667806f, 0.0f,
343 -0.179611190634277f, 0.290617011204044f, 0.894427191000000f,
344 -0.179611190628666f, -0.760845213037948f, 0.552786404498399f,
345 -0.179611190628666f, -0.760845213037948f, -0.552786404498399f,
346 -0.179611190634277f, 0.290617011204044f, -0.894427191000000f,
347 -0.760845213036861f, 0.179611190632978f, -0.552786404500000f,
348 -0.760845213033849f, 0.581234022404097f, 0.0f,
349 -0.760845213036861f, 0.179611190632978f, 0.552786404500000f,
350 -0.760845213037948f, -0.470228201835026f, 0.341640786498800f,
351 -0.760845213037948f, -0.470228201835026f, -0.341640786498800f,
355 static GLubyte icosahedron_vi[ICOSAHEDRON_VERT_PER_OBJ] =
378 DECLARE_SHAPE_CACHE(icosahedron,Icosahedron,ICOSAHEDRON);
380 /* -- Octahedron -- */
381 #define OCTAHEDRON_NUM_VERT 6
382 #define OCTAHEDRON_NUM_FACES 8
383 #define OCTAHEDRON_NUM_EDGE_PER_FACE 3
384 #define OCTAHEDRON_VERT_PER_OBJ (OCTAHEDRON_NUM_FACES*OCTAHEDRON_NUM_EDGE_PER_FACE)
385 #define OCTAHEDRON_VERT_ELEM_PER_OBJ (OCTAHEDRON_VERT_PER_OBJ*3)
386 #define OCTAHEDRON_VERT_PER_OBJ_TRI OCTAHEDRON_VERT_PER_OBJ
388 /* Vertex Coordinates */
389 static GLfloat octahedron_v[OCTAHEDRON_NUM_VERT*3] =
400 static GLfloat octahedron_n[OCTAHEDRON_NUM_FACES*3] =
402 0.577350269189f, 0.577350269189f, 0.577350269189f, /* sqrt(1/3) */
403 0.577350269189f, 0.577350269189f,-0.577350269189f,
404 0.577350269189f,-0.577350269189f, 0.577350269189f,
405 0.577350269189f,-0.577350269189f,-0.577350269189f,
406 -0.577350269189f, 0.577350269189f, 0.577350269189f,
407 -0.577350269189f, 0.577350269189f,-0.577350269189f,
408 -0.577350269189f,-0.577350269189f, 0.577350269189f,
409 -0.577350269189f,-0.577350269189f,-0.577350269189f
414 static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OBJ] =
425 DECLARE_SHAPE_CACHE(octahedron,Octahedron,OCTAHEDRON);
427 /* -- RhombicDodecahedron -- */
428 #define RHOMBICDODECAHEDRON_NUM_VERT 14
429 #define RHOMBICDODECAHEDRON_NUM_FACES 12
430 #define RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE 4
431 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ (RHOMBICDODECAHEDRON_NUM_FACES*RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE)
432 #define RHOMBICDODECAHEDRON_VERT_ELEM_PER_OBJ (RHOMBICDODECAHEDRON_VERT_PER_OBJ*3)
433 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ_TRI (RHOMBICDODECAHEDRON_VERT_PER_OBJ+RHOMBICDODECAHEDRON_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
435 /* Vertex Coordinates */
436 static GLfloat rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] =
439 0.707106781187f, 0.0f, 0.5f,
440 0.0f, 0.707106781187f, 0.5f,
441 -0.707106781187f, 0.0f, 0.5f,
442 0.0f, -0.707106781187f, 0.5f,
443 0.707106781187f, 0.707106781187f, 0.0f,
444 -0.707106781187f, 0.707106781187f, 0.0f,
445 -0.707106781187f, -0.707106781187f, 0.0f,
446 0.707106781187f, -0.707106781187f, 0.0f,
447 0.707106781187f, 0.0f, -0.5f,
448 0.0f, 0.707106781187f, -0.5f,
449 -0.707106781187f, 0.0f, -0.5f,
450 0.0f, -0.707106781187f, -0.5f,
454 static GLfloat rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] =
456 0.353553390594f, 0.353553390594f, 0.5f,
457 -0.353553390594f, 0.353553390594f, 0.5f,
458 -0.353553390594f, -0.353553390594f, 0.5f,
459 0.353553390594f, -0.353553390594f, 0.5f,
464 0.353553390594f, 0.353553390594f, -0.5f,
465 -0.353553390594f, 0.353553390594f, -0.5f,
466 -0.353553390594f, -0.353553390594f, -0.5f,
467 0.353553390594f, -0.353553390594f, -0.5f
471 static GLubyte rhombicdodecahedron_vi[RHOMBICDODECAHEDRON_VERT_PER_OBJ] =
486 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON);
488 /* -- Tetrahedron -- */
489 /* Magic Numbers: r0 = ( 1, 0, 0 )
490 * r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
491 * r2 = ( -1/3, - sqrt(2) / 3, sqrt(6) / 3 )
492 * r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
493 * |r0| = |r1| = |r2| = |r3| = 1
494 * Distance between any two points is 2 sqrt(6) / 3
496 * Normals: The unit normals are simply the negative of the coordinates of the point not on the surface.
498 #define TETRAHEDRON_NUM_VERT 4
499 #define TETRAHEDRON_NUM_FACES 4
500 #define TETRAHEDRON_NUM_EDGE_PER_FACE 3
501 #define TETRAHEDRON_VERT_PER_OBJ (TETRAHEDRON_NUM_FACES*TETRAHEDRON_NUM_EDGE_PER_FACE)
502 #define TETRAHEDRON_VERT_ELEM_PER_OBJ (TETRAHEDRON_VERT_PER_OBJ*3)
503 #define TETRAHEDRON_VERT_PER_OBJ_TRI TETRAHEDRON_VERT_PER_OBJ
505 /* Vertex Coordinates */
506 static GLfloat tetrahedron_v[TETRAHEDRON_NUM_VERT*3] =
509 -0.333333333333f, 0.942809041582f, 0.0f,
510 -0.333333333333f, -0.471404520791f, 0.816496580928f,
511 -0.333333333333f, -0.471404520791f, -0.816496580928f
514 static GLfloat tetrahedron_n[TETRAHEDRON_NUM_FACES*3] =
517 0.333333333333f, -0.942809041582f, 0.0f,
518 0.333333333333f, 0.471404520791f, -0.816496580928f,
519 0.333333333333f, 0.471404520791f, 0.816496580928f
523 static GLubyte tetrahedron_vi[TETRAHEDRON_VERT_PER_OBJ] =
530 DECLARE_SHAPE_CACHE(tetrahedron,Tetrahedron,TETRAHEDRON);
532 /* -- Sierpinski Sponge -- */
533 static unsigned int ipow (int x, unsigned int y)
535 return y==0? 1: y==1? x: (y%2? x: 1) * ipow(x*x, y/2);
538 static void fghSierpinskiSpongeGenerate ( int numLevels, double offset[3], GLfloat scale, GLfloat* vertices, GLfloat* normals )
541 if ( numLevels == 0 )
543 for (i=0; i<TETRAHEDRON_NUM_FACES; i++)
546 int faceIdxVertIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE;
547 for (j=0; j<TETRAHEDRON_NUM_EDGE_PER_FACE; j++)
549 int outIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE*3+j*3;
550 int vertIdx = tetrahedron_vi[faceIdxVertIdx+j]*3;
552 vertices[outIdx ] = (GLfloat)offset[0] + scale * tetrahedron_v[vertIdx ];
553 vertices[outIdx+1] = (GLfloat)offset[1] + scale * tetrahedron_v[vertIdx+1];
554 vertices[outIdx+2] = (GLfloat)offset[2] + scale * tetrahedron_v[vertIdx+2];
556 normals [outIdx ] = tetrahedron_n[normIdx ];
557 normals [outIdx+1] = tetrahedron_n[normIdx+1];
558 normals [outIdx+2] = tetrahedron_n[normIdx+2];
562 else if ( numLevels > 0 )
564 double local_offset[3] ; /* Use a local variable to avoid buildup of roundoff errors */
565 unsigned int stride = ipow(4,--numLevels)*TETRAHEDRON_VERT_ELEM_PER_OBJ;
567 for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ )
570 local_offset[0] = offset[0] + scale * tetrahedron_v[idx ];
571 local_offset[1] = offset[1] + scale * tetrahedron_v[idx+1];
572 local_offset[2] = offset[2] + scale * tetrahedron_v[idx+2];
573 fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride );
578 /* -- Now the various shapes involving circles -- */
580 * Compute lookup table of cos and sin values forming a circle
581 * (or half circle if halfCircle==TRUE)
584 * It is the responsibility of the caller to free these tables
585 * The size of the table is (n+1) to form a connected loop
586 * The last entry is exactly the same as the first
587 * The sign of n can be flipped to get the reverse loop
589 static void fghCircleTable(GLfloat **sint, GLfloat **cost, const int n, const GLboolean halfCircle)
593 /* Table size, the sign of n flips the circle direction */
594 const int size = abs(n);
596 /* Determine the angle between samples */
597 const GLfloat angle = (halfCircle?1:2)*(GLfloat)M_PI/(GLfloat)( ( n == 0 ) ? 1 : n );
599 /* Allocate memory for n samples, plus duplicate of first entry at the end */
600 *sint = malloc(sizeof(GLfloat) * (size+1));
601 *cost = malloc(sizeof(GLfloat) * (size+1));
603 /* Bail out if memory allocation fails, fgError never returns */
604 if (!(*sint) || !(*cost))
608 fgError("Failed to allocate memory in fghCircleTable");
611 /* Compute cos and sin around the circle */
615 for (i=1; i<size; i++)
617 (*sint)[i] = sinf(angle*i);
618 (*cost)[i] = cosf(angle*i);
624 (*sint)[size] = 0.0f; /* sin PI */
625 (*cost)[size] = -1.0f; /* cos PI */
629 /* Last sample is duplicate of the first (sin or cos of 2 PI) */
630 (*sint)[size] = (*sint)[0];
631 (*cost)[size] = (*cost)[0];
636 /* -- INTERNAL DRAWING functions --------------------------------------- */
637 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
638 static void fgh##nameICaps( GLboolean useWireMode )\
642 fgh##nameICaps##Generate();\
643 name##Cached = GL_TRUE;\
648 fghDrawGeometryWire (name##_verts,name##_norms,\
649 nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE);\
653 fghDrawGeometrySolid(name##_verts,name##_norms,vertIdxs,\
654 nameCaps##_VERT_PER_OBJ, nameCaps##_VERT_PER_OBJ_TRI); \
657 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
658 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
660 static void fghCube( GLfloat dSize, GLboolean useWireMode )
667 cubeCached = GL_TRUE;
672 /* Need to build new vertex list containing vertices for cube of different size */
675 vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
677 /* Bail out if memory allocation fails, fgError never returns */
681 fgError("Failed to allocate memory in fghCube");
684 for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
685 vertices[i] = dSize*cube_verts[i];
688 vertices = cube_verts;
691 fghDrawGeometryWire (vertices, cube_norms,
692 CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE);
694 fghDrawGeometrySolid(vertices, cube_norms, cube_vertIdxs,
695 CUBE_VERT_PER_OBJ, CUBE_VERT_PER_OBJ_TRI);
698 /* cleanup allocated memory */
702 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON);
703 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON);
704 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON);
705 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON);
706 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON);
708 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
712 GLsizei numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
713 GLsizei numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
714 GLsizei numFace = numTetr*TETRAHEDRON_NUM_FACES;
718 /* Allocate memory */
719 vertices = malloc(numVert*3 * sizeof(GLfloat));
720 normals = malloc(numVert*3 * sizeof(GLfloat));
721 /* Bail out if memory allocation fails, fgError never returns */
722 if (!vertices || !normals)
726 fgError("Failed to allocate memory in fghSierpinskiSponge");
729 /* Generate elements */
730 fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
732 /* Draw and cleanup */
734 fghDrawGeometryWire (vertices,normals,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE);
736 fghDrawGeometrySolid(vertices,normals,NULL,numVert,numVert);
742 #endif /* GL_ES_VERSION_2_0 */
745 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
748 #ifndef EGL_VERSION_1_0
750 * Draws a solid sphere
752 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
756 /* Adjust z and radius as stacks are drawn. */
757 GLfloat radf = (GLfloat)radius;
761 /* Pre-computed circle */
763 GLfloat *sint1,*cost1;
764 GLfloat *sint2,*cost2;
766 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
768 fghCircleTable(&sint1,&cost1,-slices,FALSE);
769 fghCircleTable(&sint2,&cost2, stacks,TRUE);
771 /* The top stack is covered with a triangle fan */
774 z1 = cost2[(stacks>0)?1:0];
776 r1 = sint2[(stacks>0)?1:0];
778 glBegin(GL_TRIANGLE_FAN);
781 glVertex3f(0,0,radf);
783 for (j=slices; j>=0; j--)
785 glNormal3f(cost1[j]*r1, sint1[j]*r1, z1 );
786 glVertex3f(cost1[j]*r1*radf, sint1[j]*r1*radf, z1*radf);
791 /* Cover each stack with a quad strip, except the top and bottom stacks */
793 for( i=1; i<stacks-1; i++ )
795 z0 = z1; z1 = cost2[i+1];
796 r0 = r1; r1 = sint2[i+1];
798 glBegin(GL_QUAD_STRIP);
800 for(j=0; j<=slices; j++)
802 glNormal3d(cost1[j]*r1, sint1[j]*r1, z1 );
803 glVertex3d(cost1[j]*r1*radf, sint1[j]*r1*radf, z1*radf);
804 glNormal3d(cost1[j]*r0, sint1[j]*r0, z0 );
805 glVertex3d(cost1[j]*r0*radf, sint1[j]*r0*radf, z0*radf);
811 /* The bottom stack is covered with a triangle fan */
816 glBegin(GL_TRIANGLE_FAN);
819 glVertex3d(0,0,-radius);
821 for (j=0; j<=slices; j++)
823 glNormal3d(cost1[j]*r0, sint1[j]*r0, z0 );
824 glVertex3d(cost1[j]*r0*radf, sint1[j]*r0*radf, z0*radf);
829 /* Release sin and cos tables */
838 * Draws a wire sphere
840 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
844 /* Adjust z and radius as stacks and slices are drawn. */
845 GLfloat radf = (GLfloat)radius;
849 /* Pre-computed circle */
851 GLfloat *sint1,*cost1;
852 GLfloat *sint2,*cost2;
854 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
856 fghCircleTable(&sint1,&cost1,-slices,FALSE);
857 fghCircleTable(&sint2,&cost2, stacks,TRUE);
859 /* Draw a line loop for each stack */
861 for (i=1; i<stacks; i++)
866 glBegin(GL_LINE_LOOP);
868 for(j=0; j<=slices; j++)
874 glVertex3f(x*r*radf,y*r*radf,z*radf);
880 /* Draw a line loop for each slice */
882 for (i=0; i<slices; i++)
884 glBegin(GL_LINE_STRIP);
886 for(j=0; j<=stacks; j++)
888 x = cost1[i]*sint2[j];
889 y = sint1[i]*sint2[j];
893 glVertex3f(x*radf,y*radf,z*radf);
899 /* Release sin and cos tables */
910 void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks )
914 /* Step in z and radius as stacks are drawn. */
919 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
920 const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
922 /* Scaling factors for vertex normals */
924 const GLfloat cosn = ( (GLfloat)height / sqrtf( height * height + base * base ));
925 const GLfloat sinn = ( (GLfloat)base / sqrtf( height * height + base * base ));
927 /* Pre-computed circle */
931 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
933 fghCircleTable(&sint,&cost,-slices,FALSE);
935 /* Cover the circular base with a triangle fan... */
943 glBegin(GL_TRIANGLE_FAN);
946 glVertex3f(0,0, z0 );
948 for (j=0; j<=slices; j++)
949 glVertex3f(cost[j]*r0, sint[j]*r0, z0);
953 /* Cover each stack with a quad strip, except the top stack */
955 for( i=0; i<stacks-1; i++ )
957 glBegin(GL_QUAD_STRIP);
959 for(j=0; j<=slices; j++)
961 glNormal3f(cost[j]*cosn, sint[j]*cosn, sinn);
962 glVertex3f(cost[j]*r0, sint[j]*r0, z0 );
963 glVertex3f(cost[j]*r1, sint[j]*r1, z1 );
966 z0 = z1; z1 += zStep;
967 r0 = r1; r1 -= rStep;
972 /* The top stack is covered with individual triangles */
974 glBegin(GL_TRIANGLES);
976 glNormal3f(cost[0]*sinn, sint[0]*sinn, cosn);
978 for (j=0; j<slices; j++)
980 glVertex3f(cost[j+0]*r0, sint[j+0]*r0, z0 );
981 glVertex3f(0, 0, (GLfloat)height);
982 glNormal3f(cost[j+1]*sinn, sint[j+1]*sinn, cosn );
983 glVertex3f(cost[j+1]*r0, sint[j+1]*r0, z0 );
988 /* Release sin and cos tables */
997 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
1001 /* Step in z and radius as stacks are drawn. */
1004 GLfloat r = (GLfloat)base;
1006 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1007 const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
1009 /* Scaling factors for vertex normals */
1011 const GLfloat cosn = ( (GLfloat)height / sqrtf( height * height + base * base ));
1012 const GLfloat sinn = ( (GLfloat)base / sqrtf( height * height + base * base ));
1014 /* Pre-computed circle */
1016 GLfloat *sint,*cost;
1018 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
1020 fghCircleTable(&sint,&cost,-slices,FALSE);
1022 /* Draw the stacks... */
1024 for (i=0; i<stacks; i++)
1026 glBegin(GL_LINE_LOOP);
1028 for( j=0; j<slices; j++ )
1030 glNormal3f(cost[j]*sinn, sint[j]*sinn, cosn);
1031 glVertex3f(cost[j]*r, sint[j]*r, z );
1040 /* Draw the slices */
1046 for (j=0; j<slices; j++)
1048 glNormal3f(cost[j]*sinn, sint[j]*sinn, cosn );
1049 glVertex3f(cost[j]*r, sint[j]*r, 0 );
1050 glVertex3f(0, 0, (GLfloat)height);
1055 /* Release sin and cos tables */
1063 * Draws a solid cylinder
1065 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
1069 /* Step in z and radius as stacks are drawn. */
1070 GLfloat radf = (GLfloat)radius;
1072 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1074 /* Pre-computed circle */
1076 GLfloat *sint,*cost;
1078 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
1080 fghCircleTable(&sint,&cost,-slices,FALSE);
1082 /* Cover the base and top */
1084 glBegin(GL_TRIANGLE_FAN);
1085 glNormal3f(0, 0, -1 );
1086 glVertex3f(0, 0, 0 );
1087 for (j=0; j<=slices; j++)
1088 glVertex3f(cost[j]*radf, sint[j]*radf, 0);
1091 glBegin(GL_TRIANGLE_FAN);
1092 glNormal3f(0, 0, 1 );
1093 glVertex3f(0, 0, (GLfloat)height);
1094 for (j=slices; j>=0; j--)
1095 glVertex3f(cost[j]*radf, sint[j]*radf, (GLfloat)height);
1103 for (i=1; i<=stacks; i++)
1106 z1 = (GLfloat)height;
1108 glBegin(GL_QUAD_STRIP);
1109 for (j=0; j<=slices; j++ )
1111 glNormal3f(cost[j], sint[j], 0 );
1112 glVertex3f(cost[j]*radf, sint[j]*radf, z0 );
1113 glVertex3f(cost[j]*radf, sint[j]*radf, z1 );
1117 z0 = z1; z1 += zStep;
1120 /* Release sin and cos tables */
1127 * Draws a wire cylinder
1129 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
1133 /* Step in z and radius as stacks are drawn. */
1134 GLfloat radf = (GLfloat)radius;
1136 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1138 /* Pre-computed circle */
1140 GLfloat *sint,*cost;
1142 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
1144 fghCircleTable(&sint,&cost,-slices,FALSE);
1146 /* Draw the stacks... */
1148 for (i=0; i<=stacks; i++)
1151 z = (GLfloat)height;
1153 glBegin(GL_LINE_LOOP);
1155 for( j=0; j<slices; j++ )
1157 glNormal3f(cost[j], sint[j], 0);
1158 glVertex3f(cost[j]*radf, sint[j]*radf, z);
1166 /* Draw the slices */
1170 for (j=0; j<slices; j++)
1172 glNormal3f(cost[j], sint[j], 0 );
1173 glVertex3f(cost[j]*radf, sint[j]*radf, 0 );
1174 glVertex3f(cost[j]*radf, sint[j]*radf, (GLfloat)height);
1179 /* Release sin and cos tables */
1186 * Draws a wire torus
1188 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1190 GLfloat iradius = (float)dInnerRadius, oradius = (float)dOuterRadius;
1191 GLfloat phi, psi, dpsi, dphi;
1192 GLfloat *vertex, *normal;
1194 GLfloat spsi, cpsi, sphi, cphi ;
1196 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
1198 if ( nSides < 1 ) nSides = 1;
1199 if ( nRings < 1 ) nRings = 1;
1201 /* Allocate the vertices array */
1202 vertex = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1203 normal = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1207 dpsi = 2.0f * (GLfloat)M_PI / (GLfloat)(nRings) ;
1208 dphi = -2.0f * (GLfloat)M_PI / (GLfloat)(nSides) ;
1211 for( j=0; j<nRings; j++ )
1213 cpsi = cosf( psi ) ;
1214 spsi = sinf( psi ) ;
1217 for( i=0; i<nSides; i++ )
1219 int offset = 3 * ( j * nSides + i ) ;
1220 cphi = cosf( phi ) ;
1221 sphi = sinf( phi ) ;
1222 *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1223 *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1224 *(vertex + offset + 2) = sphi * iradius ;
1225 *(normal + offset + 0) = cpsi * cphi ;
1226 *(normal + offset + 1) = spsi * cphi ;
1227 *(normal + offset + 2) = sphi ;
1234 for( i=0; i<nSides; i++ )
1236 glBegin( GL_LINE_LOOP );
1238 for( j=0; j<nRings; j++ )
1240 int offset = 3 * ( j * nSides + i ) ;
1241 glNormal3fv( normal + offset );
1242 glVertex3fv( vertex + offset );
1248 for( j=0; j<nRings; j++ )
1250 glBegin(GL_LINE_LOOP);
1252 for( i=0; i<nSides; i++ )
1254 int offset = 3 * ( j * nSides + i ) ;
1255 glNormal3fv( normal + offset );
1256 glVertex3fv( vertex + offset );
1268 * Draws a solid torus
1270 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
1272 GLfloat iradius = (float)dInnerRadius, oradius = (float)dOuterRadius;
1273 GLfloat phi, psi, dpsi, dphi;
1274 GLfloat *vertex, *normal;
1276 GLfloat spsi, cpsi, sphi, cphi ;
1278 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
1280 if ( nSides < 1 ) nSides = 1;
1281 if ( nRings < 1 ) nRings = 1;
1283 /* Increment the number of sides and rings to allow for one more point than surface */
1287 /* Allocate the vertices array */
1288 vertex = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1289 normal = (GLfloat *)calloc( sizeof(GLfloat), 3 * nSides * nRings );
1293 dpsi = 2.0f * (GLfloat)M_PI / (GLfloat)(nRings - 1) ;
1294 dphi = -2.0f * (GLfloat)M_PI / (GLfloat)(nSides - 1) ;
1297 for( j=0; j<nRings; j++ )
1299 cpsi = cosf( psi ) ;
1300 spsi = sinf( psi ) ;
1303 for( i=0; i<nSides; i++ )
1305 int offset = 3 * ( j * nSides + i ) ;
1306 cphi = cosf( phi ) ;
1307 sphi = sinf( phi ) ;
1308 *(vertex + offset + 0) = cpsi * ( oradius + cphi * iradius ) ;
1309 *(vertex + offset + 1) = spsi * ( oradius + cphi * iradius ) ;
1310 *(vertex + offset + 2) = sphi * iradius ;
1311 *(normal + offset + 0) = cpsi * cphi ;
1312 *(normal + offset + 1) = spsi * cphi ;
1313 *(normal + offset + 2) = sphi ;
1320 glBegin( GL_QUADS );
1321 for( i=0; i<nSides-1; i++ )
1323 for( j=0; j<nRings-1; j++ )
1325 int offset = 3 * ( j * nSides + i ) ;
1326 glNormal3fv( normal + offset );
1327 glVertex3fv( vertex + offset );
1328 glNormal3fv( normal + offset + 3 );
1329 glVertex3fv( vertex + offset + 3 );
1330 glNormal3fv( normal + offset + 3 * nSides + 3 );
1331 glVertex3fv( vertex + offset + 3 * nSides + 3 );
1332 glNormal3fv( normal + offset + 3 * nSides );
1333 glVertex3fv( vertex + offset + 3 * nSides );
1343 #endif /* EGL_VERSION_1_0 */
1347 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
1348 /* Macro to generate interface functions */
1349 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
1350 void FGAPIENTRY glutWire##nameICaps( void )\
1352 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
1353 fgh##nameICaps( TRUE );\
1355 void FGAPIENTRY glutSolid##nameICaps( void )\
1357 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
1358 fgh##nameICaps( FALSE );\
1361 void FGAPIENTRY glutWireCube( double dSize )
1363 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
1364 fghCube( (GLfloat)dSize, TRUE );
1366 void FGAPIENTRY glutSolidCube( double dSize )
1368 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
1369 fghCube( (GLfloat)dSize, FALSE );
1372 DECLARE_SHAPE_INTERFACE(Dodecahedron);
1373 DECLARE_SHAPE_INTERFACE(Icosahedron);
1374 DECLARE_SHAPE_INTERFACE(Octahedron);
1375 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron);
1377 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
1379 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
1380 fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, TRUE );
1382 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
1384 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
1385 fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, FALSE );
1388 DECLARE_SHAPE_INTERFACE(Tetrahedron);
1391 /*** END OF FILE ***/