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"
34 * A note: We do not use the GLuint data type for vertex index arrays
35 * in this code as Open GL ES1 only supports GLushort. This affects the
36 * cylindrical objects only (Torus, Sphere, Cylinder and Cone) and limits
37 * their number of vertices to 65535 (2^16-1). Thats about 256*256
38 * subdivisions, which is sufficient for just about any usage case, so
39 * I am not going to worry about it for now.
40 * One could do compile time detection of the gluint type through CMake,
41 * but it is likely that we'll eventually move to runtime selection
42 * of OpenGL or GLES1/2, which would make that strategy useless...
45 /* declare for drawing using the different OpenGL versions here so we can
46 have a nice code order below */
47 #ifndef GL_ES_VERSION_2_0
48 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
49 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
50 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
52 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
53 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
55 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
56 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
57 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
58 GLint attribute_v_coord, GLint attribute_v_normal
60 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
61 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
62 GLint attribute_v_coord, GLint attribute_v_normal);
63 /* declare function for generating visualization of normals */
64 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
65 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
66 #ifndef GL_ES_VERSION_2_0
67 static void fghDrawNormalVisualization11();
69 static void fghDrawNormalVisualization20(GLint attribute_v_coord);
72 * Explanation of the functions has to be separate for the polyhedra and
73 * the non-polyhedra (objects with a circular cross-section).
75 * - We have only implemented the five platonic solids and the rhomboid
76 * dodecahedron. If you need more types of polyhedra, please see
78 * - Solids are drawn by glDrawArrays if composed of triangular faces
79 * (the tetrahedron, octahedron, and icosahedron), or are first
80 * decomposed into triangles and then drawn by glDrawElements if its
81 * faces are squares or pentagons (cube, dodecahedron and rhombic
82 * dodecahedron) as some vertices are repeated in that case.
83 * - WireFrame drawing is done using a GL_LINE_LOOP per face, and thus
84 * issuing one draw call per face. glDrawArrays is always used as no
85 * triangle decomposition is needed to draw faces. We use the "first"
86 * parameter in glDrawArrays to go from face to face.
89 * - We have implemented the sphere, cylinder, cone and torus.
90 * - All shapes are characterized by two parameters: the number of
91 * subdivisions along two axes used to construct the shape's vertices
92 * (e.g. stacks and slices for the sphere).
93 * As different subdivisions are most suitable for different shapes,
94 * and are thus also named differently, I wont provide general comments
96 * - Solids are drawn using glDrawArrays and GL_TRIANGLE_STRIP. Each
97 * strip covers one revolution around one of the two subdivision axes
99 * - WireFrame drawing is done for the subdivisions along the two axes
100 * separately, usually using GL_LINE_LOOP. Vertex index arrays are
101 * built containing the vertices to be drawn for each loop, which are
102 * then drawn using multiple calls to glDrawElements. As the number of
103 * subdivisions along the two axes is not guaranteed to be equal, the
104 * vertex indices for e.g. stacks and slices are stored in separate
105 * arrays, which makes the input to the drawing function a bit clunky,
106 * but allows for the same drawing function to be used for all shapes.
111 * Draw geometric shape in wire mode (only edges)
114 * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
115 * The vertex coordinate and normal buffers, and the number of entries in
118 * a vertex indices buffer, optional (never passed for the polyhedra)
119 * GLsizei numParts, GLsizei numVertPerPart
120 * polyhedra: number of faces, and the number of vertices for drawing
122 * non-polyhedra: number of edges to draw for first subdivision (not
123 * necessarily equal to number of subdivisions requested by user, e.g.
124 * as each subdivision is enclosed by two edges), and number of
125 * vertices for drawing each
126 * numParts * numVertPerPart gives the number of entries in the vertex
129 * vertex drawing mode (e.g. always GL_LINE_LOOP for polyhedra, varies
131 * GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
132 * non-polyhedra only: same as the above, but now for subdivisions along
133 * the other axis. Always drawn as GL_LINE_LOOP.
135 * Feel free to contribute better naming ;)
137 static void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
138 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
139 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
142 GLint attribute_v_coord = fgStructure.CurrentWindow->Window.attribute_v_coord;
143 GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal;
145 if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
146 /* User requested a 2.0 draw */
147 fghDrawGeometryWire20(vertices, normals, numVertices,
148 vertIdxs, numParts, numVertPerPart, vertexMode,
149 vertIdxs2, numParts2, numVertPerPart2,
150 attribute_v_coord, attribute_v_normal);
151 #ifndef GL_ES_VERSION_2_0
153 fghDrawGeometryWire11(vertices, normals,
154 vertIdxs, numParts, numVertPerPart, vertexMode,
155 vertIdxs2, numParts2, numVertPerPart2);
159 /* Draw the geometric shape with filled triangles
162 * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
163 * The vertex coordinate and normal buffers, and the number of entries in
166 * a vertex indices buffer, optional (not passed for the polyhedra with
168 * GLsizei numParts, GLsizei numVertPerPart
169 * polyhedra: not used for polyhedra with triangular faces
170 (numEdgePerFace==3), as each vertex+normal pair is drawn only once,
171 so no vertex indices are used.
172 Else, the shape was triangulated (DECOMPOSE_TO_TRIANGLE), leading to
173 reuse of some vertex+normal pairs, and thus the need to draw with
174 glDrawElements. numParts is always 1 in this case (we can draw the
175 whole object with one call to glDrawElements as the vertex index
176 array contains separate triangles), and numVertPerPart indicates
177 the number of vertex indices in the vertex array.
178 * non-polyhedra: number of parts (GL_TRIANGLE_STRIPs) to be drawn
179 separately (numParts calls to glDrawElements) to create the object.
180 numVertPerPart indicates the number of vertex indices to be
181 processed at each draw call.
182 * numParts * numVertPerPart gives the number of entries in the vertex
185 static void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
186 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
188 GLint attribute_v_coord = fgStructure.CurrentWindow->Window.attribute_v_coord;
189 GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal;
191 if (fgStructure.CurrentWindow->State.VisualizeNormals)
192 /* generate normals for each vertex to be drawn as well */
193 fghGenerateNormalVisualization(vertices, normals, numVertices,
194 vertIdxs, numParts, numVertIdxsPerPart);
196 if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
198 /* User requested a 2.0 draw */
199 fghDrawGeometrySolid20(vertices, normals, numVertices,
200 vertIdxs, numParts, numVertIdxsPerPart,
201 attribute_v_coord, attribute_v_normal);
203 if (fgStructure.CurrentWindow->State.VisualizeNormals)
204 /* draw normals for each vertex as well */
205 fghDrawNormalVisualization20(attribute_v_coord);
207 #ifndef GL_ES_VERSION_2_0
210 fghDrawGeometrySolid11(vertices, normals, numVertices,
211 vertIdxs, numParts, numVertIdxsPerPart);
213 if (fgStructure.CurrentWindow->State.VisualizeNormals)
214 /* draw normals for each vertex as well */
215 fghDrawNormalVisualization11();
222 /* Version for OpenGL (ES) 1.1 */
223 #ifndef GL_ES_VERSION_2_0
224 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
225 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
226 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
231 glEnableClientState(GL_VERTEX_ARRAY);
232 glEnableClientState(GL_NORMAL_ARRAY);
234 glVertexPointer(3, GL_FLOAT, 0, vertices);
235 glNormalPointer(GL_FLOAT, 0, normals);
239 /* Draw per face (TODO: could use glMultiDrawArrays if available) */
240 for (i=0; i<numParts; i++)
241 glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
243 for (i=0; i<numParts; i++)
244 glDrawElements(vertexMode,numVertPerPart,GL_UNSIGNED_SHORT,vertIdxs+i*numVertPerPart);
247 for (i=0; i<numParts2; i++)
248 glDrawElements(GL_LINE_LOOP,numVertPerPart2,GL_UNSIGNED_SHORT,vertIdxs2+i*numVertPerPart2);
250 glDisableClientState(GL_VERTEX_ARRAY);
251 glDisableClientState(GL_NORMAL_ARRAY);
255 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
256 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
260 glEnableClientState(GL_VERTEX_ARRAY);
261 glEnableClientState(GL_NORMAL_ARRAY);
263 glVertexPointer(3, GL_FLOAT, 0, vertices);
264 glNormalPointer(GL_FLOAT, 0, normals);
267 glDrawArrays(GL_TRIANGLES, 0, numVertices);
270 for (i=0; i<numParts; i++)
271 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs+i*numVertIdxsPerPart);
273 glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs);
275 glDisableClientState(GL_VERTEX_ARRAY);
276 glDisableClientState(GL_NORMAL_ARRAY);
280 /* Version for OpenGL (ES) >= 2.0 */
281 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
282 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
283 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
284 GLint attribute_v_coord, GLint attribute_v_normal)
286 GLuint vbo_coords = 0, vbo_normals = 0,
287 ibo_elements = 0, ibo_elements2 = 0;
288 GLsizei numVertIdxs = numParts * numVertPerPart;
289 GLsizei numVertIdxs2 = numParts2 * numVertPerPart2;
292 if (numVertices > 0 && attribute_v_coord != -1) {
293 fghGenBuffers(1, &vbo_coords);
294 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
295 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
296 vertices, FGH_STATIC_DRAW);
299 if (numVertices > 0 && attribute_v_normal != -1) {
300 fghGenBuffers(1, &vbo_normals);
301 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
302 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
303 normals, FGH_STATIC_DRAW);
306 if (vertIdxs != NULL) {
307 fghGenBuffers(1, &ibo_elements);
308 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
309 fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
310 vertIdxs, FGH_STATIC_DRAW);
311 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
314 if (vertIdxs2 != NULL) {
315 fghGenBuffers(1, &ibo_elements2);
316 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
317 fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs2 * sizeof(vertIdxs2[0]),
318 vertIdxs2, FGH_STATIC_DRAW);
319 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
323 fghEnableVertexAttribArray(attribute_v_coord);
324 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
325 fghVertexAttribPointer(
326 attribute_v_coord, /* attribute */
327 3, /* number of elements per vertex, here (x,y,z) */
328 GL_FLOAT, /* the type of each element */
329 GL_FALSE, /* take our values as-is */
330 0, /* no extra data between each position */
331 0 /* offset of first element */
333 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
337 fghEnableVertexAttribArray(attribute_v_normal);
338 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
339 fghVertexAttribPointer(
340 attribute_v_normal, /* attribute */
341 3, /* number of elements per vertex, here (x,y,z) */
342 GL_FLOAT, /* the type of each element */
343 GL_FALSE, /* take our values as-is */
344 0, /* no extra data between each position */
345 0 /* offset of first element */
347 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
351 /* Draw per face (TODO: could use glMultiDrawArrays if available) */
352 for (i=0; i<numParts; i++)
353 glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
355 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
356 for (i=0; i<numParts; i++)
357 glDrawElements(vertexMode, numVertPerPart,
358 GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertPerPart));
359 /* Clean existing bindings before clean-up */
360 /* Android showed instability otherwise */
361 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
365 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
366 for (i=0; i<numParts2; i++)
367 glDrawElements(GL_LINE_LOOP, numVertPerPart2,
368 GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs2[0])*i*numVertPerPart2));
369 /* Clean existing bindings before clean-up */
370 /* Android showed instability otherwise */
371 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
375 fghDisableVertexAttribArray(attribute_v_coord);
376 if (vbo_normals != 0)
377 fghDisableVertexAttribArray(attribute_v_normal);
380 fghDeleteBuffers(1, &vbo_coords);
381 if (vbo_normals != 0)
382 fghDeleteBuffers(1, &vbo_normals);
383 if (ibo_elements != 0)
384 fghDeleteBuffers(1, &ibo_elements);
385 if (ibo_elements2 != 0)
386 fghDeleteBuffers(1, &ibo_elements2);
392 /* Version for OpenGL (ES) >= 2.0 */
393 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
394 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
395 GLint attribute_v_coord, GLint attribute_v_normal)
397 GLuint vbo_coords = 0, vbo_normals = 0, ibo_elements = 0;
398 GLsizei numVertIdxs = numParts * numVertIdxsPerPart;
401 if (numVertices > 0 && attribute_v_coord != -1) {
402 fghGenBuffers(1, &vbo_coords);
403 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
404 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
405 vertices, FGH_STATIC_DRAW);
406 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
409 if (numVertices > 0 && attribute_v_normal != -1) {
410 fghGenBuffers(1, &vbo_normals);
411 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
412 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
413 normals, FGH_STATIC_DRAW);
414 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
417 if (vertIdxs != NULL) {
418 fghGenBuffers(1, &ibo_elements);
419 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
420 fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
421 vertIdxs, FGH_STATIC_DRAW);
422 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
426 fghEnableVertexAttribArray(attribute_v_coord);
427 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
428 fghVertexAttribPointer(
429 attribute_v_coord, /* attribute */
430 3, /* number of elements per vertex, here (x,y,z) */
431 GL_FLOAT, /* the type of each element */
432 GL_FALSE, /* take our values as-is */
433 0, /* no extra data between each position */
434 0 /* offset of first element */
436 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
440 fghEnableVertexAttribArray(attribute_v_normal);
441 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
442 fghVertexAttribPointer(
443 attribute_v_normal, /* attribute */
444 3, /* number of elements per vertex, here (x,y,z) */
445 GL_FLOAT, /* the type of each element */
446 GL_FALSE, /* take our values as-is */
447 0, /* no extra data between each position */
448 0 /* offset of first element */
450 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
453 if (vertIdxs == NULL) {
454 glDrawArrays(GL_TRIANGLES, 0, numVertices);
456 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
458 for (i=0; i<numParts; i++) {
459 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertIdxsPerPart));
462 glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, 0);
464 /* Clean existing bindings before clean-up */
465 /* Android showed instability otherwise */
466 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
470 fghDisableVertexAttribArray(attribute_v_coord);
471 if (vbo_normals != 0)
472 fghDisableVertexAttribArray(attribute_v_normal);
475 fghDeleteBuffers(1, &vbo_coords);
476 if (vbo_normals != 0)
477 fghDeleteBuffers(1, &vbo_normals);
478 if (ibo_elements != 0)
479 fghDeleteBuffers(1, &ibo_elements);
485 * Generate vertex indices for visualizing the normals.
487 static GLfloat *verticesForNormalVisualization;
488 static GLushort numNormalVertices = 0;
489 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
490 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
493 /* calc number of vertices to generate, allocate. Must be freed by caller
494 * We do the free at the end of fghDrawNormalVisualization11/fghDrawNormalVisualization20
497 numNormalVertices = numVertices * 2;
499 numNormalVertices = numParts * numVertIdxsPerPart * 2;
500 verticesForNormalVisualization = malloc(numNormalVertices*3 * sizeof(GLfloat));
502 /* Now generate vertices for lines to draw the normals */
505 for (i=0,j=0; i<numNormalVertices*3/2; i+=3, j+=6)
507 verticesForNormalVisualization[j+0] = vertices[i+0];
508 verticesForNormalVisualization[j+1] = vertices[i+1];
509 verticesForNormalVisualization[j+2] = vertices[i+2];
510 verticesForNormalVisualization[j+3] = vertices[i+0] + normals[i+0]/4.f;
511 verticesForNormalVisualization[j+4] = vertices[i+1] + normals[i+1]/4.f;
512 verticesForNormalVisualization[j+5] = vertices[i+2] + normals[i+2]/4.f;
517 for (i=0,j=0; i<numNormalVertices/2; i++, j+=6)
519 GLushort idx = vertIdxs[i]*3;
520 verticesForNormalVisualization[j+0] = vertices[idx+0];
521 verticesForNormalVisualization[j+1] = vertices[idx+1];
522 verticesForNormalVisualization[j+2] = vertices[idx+2];
523 verticesForNormalVisualization[j+3] = vertices[idx+0] + normals[idx+0]/4.f;
524 verticesForNormalVisualization[j+4] = vertices[idx+1] + normals[idx+1]/4.f;
525 verticesForNormalVisualization[j+5] = vertices[idx+2] + normals[idx+2]/4.f;
530 /* Version for OpenGL (ES) 1.1 */
531 #ifndef GL_ES_VERSION_2_0
532 static void fghDrawNormalVisualization11()
534 GLfloat currentColor[4];
535 /* Setup draw color: (1,1,1)-shape's color */
536 glGetFloatv(GL_CURRENT_COLOR,currentColor);
537 glColor4f(1-currentColor[0],1-currentColor[1],1-currentColor[2],currentColor[3]);
539 glEnableClientState(GL_VERTEX_ARRAY);
541 glVertexPointer(3, GL_FLOAT, 0, verticesForNormalVisualization);
542 glDrawArrays(GL_LINES, 0, numNormalVertices);
544 glDisableClientState(GL_VERTEX_ARRAY);
546 /* Done, free memory, reset color */
547 free(verticesForNormalVisualization);
548 glColor4fv(currentColor);
552 /* Version for OpenGL (ES) >= 2.0 */
553 static void fghDrawNormalVisualization20(GLint attribute_v_coord)
555 GLuint vbo_coords = 0;
557 if (attribute_v_coord != -1) {
558 fghGenBuffers(1, &vbo_coords);
559 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
560 fghBufferData(FGH_ARRAY_BUFFER, numNormalVertices * 3 * sizeof(verticesForNormalVisualization[0]),
561 verticesForNormalVisualization, FGH_STATIC_DRAW);
566 fghEnableVertexAttribArray(attribute_v_coord);
567 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
568 fghVertexAttribPointer(
569 attribute_v_coord, /* attribute */
570 3, /* number of elements per vertex, here (x,y,z) */
571 GL_FLOAT, /* the type of each element */
572 GL_FALSE, /* take our values as-is */
573 0, /* no extra data between each position */
574 0 /* offset of first element */
576 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
579 glDrawArrays(GL_LINES, 0, numNormalVertices);
582 fghDisableVertexAttribArray(attribute_v_coord);
585 fghDeleteBuffers(1, &vbo_coords);
587 /* Done, free memory */
588 free(verticesForNormalVisualization);
592 * Generate all combinations of vertices and normals needed to draw object.
593 * Optional shape decomposition to triangles:
594 * We'll use glDrawElements to draw all shapes that are not naturally
595 * composed of triangles, so generate an index vector here, using the
596 * below sampling scheme.
597 * Be careful to keep winding of all triangles counter-clockwise,
598 * assuming that input has correct winding...
600 static GLubyte vert4Decomp[6] = {0,1,2, 0,2,3}; /* quad : 4 input vertices, 6 output (2 triangles) */
601 static GLubyte vert5Decomp[9] = {0,1,2, 0,2,4, 4,2,3}; /* pentagon: 5 input vertices, 9 output (3 triangles) */
603 static void fghGenerateGeometryWithIndexArray(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut, GLushort *vertIdxOut)
605 int i,j,numEdgeIdxPerFace;
606 GLubyte *vertSamps = NULL;
607 switch (numEdgePerFace)
610 /* nothing to do here, we'll draw with glDrawArrays */
613 vertSamps = vert4Decomp;
614 numEdgeIdxPerFace = 6; /* 6 output vertices for each face */
617 vertSamps = vert5Decomp;
618 numEdgeIdxPerFace = 9; /* 9 output vertices for each face */
622 * Build array with vertices using vertex coordinates and vertex indices
623 * Do same for normals.
624 * Need to do this because of different normals at shared vertices.
626 for (i=0; i<numFaces; i++)
629 int faceIdxVertIdx = i*numEdgePerFace; /* index to first element of "row" in vertex indices */
630 for (j=0; j<numEdgePerFace; j++)
632 int outIdx = i*numEdgePerFace*3+j*3;
633 int vertIdx = vertIndices[faceIdxVertIdx+j]*3;
635 vertOut[outIdx ] = vertices[vertIdx ];
636 vertOut[outIdx+1] = vertices[vertIdx+1];
637 vertOut[outIdx+2] = vertices[vertIdx+2];
639 normOut[outIdx ] = normals [normIdx ];
640 normOut[outIdx+1] = normals [normIdx+1];
641 normOut[outIdx+2] = normals [normIdx+2];
644 /* generate vertex indices for each face */
646 for (j=0; j<numEdgeIdxPerFace; j++)
647 vertIdxOut[i*numEdgeIdxPerFace+j] = faceIdxVertIdx + vertSamps[j];
651 static void fghGenerateGeometry(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut)
653 /* This function does the same as fghGenerateGeometryWithIndexArray, just skipping the index array generation... */
654 fghGenerateGeometryWithIndexArray(numFaces, numEdgePerFace, vertices, vertIndices, normals, vertOut, normOut, NULL);
658 /* -- INTERNAL SETUP OF GEOMETRY --------------------------------------- */
659 /* -- stuff that can be cached -- */
660 /* Cache of input to glDrawArrays or glDrawElements
661 * In general, we build arrays with all vertices or normals.
662 * We cant compress this and use glDrawElements as all combinations of
663 * vertices and normals are unique.
665 #define DECLARE_SHAPE_CACHE(name,nameICaps,nameCaps)\
666 static GLboolean name##Cached = GL_FALSE;\
667 static GLfloat name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
668 static GLfloat name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
669 static void fgh##nameICaps##Generate()\
671 fghGenerateGeometry(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
672 name##_v, name##_vi, name##_n,\
673 name##_verts, name##_norms);\
675 #define DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(name,nameICaps,nameCaps)\
676 static GLboolean name##Cached = GL_FALSE;\
677 static GLfloat name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
678 static GLfloat name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
679 static GLushort name##_vertIdxs[nameCaps##_VERT_PER_OBJ_TRI];\
680 static void fgh##nameICaps##Generate()\
682 fghGenerateGeometryWithIndexArray(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
683 name##_v, name##_vi, name##_n,\
684 name##_verts, name##_norms, name##_vertIdxs);\
688 #define CUBE_NUM_VERT 8
689 #define CUBE_NUM_FACES 6
690 #define CUBE_NUM_EDGE_PER_FACE 4
691 #define CUBE_VERT_PER_OBJ (CUBE_NUM_FACES*CUBE_NUM_EDGE_PER_FACE)
692 #define CUBE_VERT_ELEM_PER_OBJ (CUBE_VERT_PER_OBJ*3)
693 #define CUBE_VERT_PER_OBJ_TRI (CUBE_VERT_PER_OBJ+CUBE_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
694 /* Vertex Coordinates */
695 static GLfloat cube_v[CUBE_NUM_VERT*3] =
707 static GLfloat cube_n[CUBE_NUM_FACES*3] =
717 /* Vertex indices, as quads, before triangulation */
718 static GLubyte cube_vi[CUBE_VERT_PER_OBJ] =
727 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(cube,Cube,CUBE)
729 /* -- Dodecahedron -- */
730 /* Magic Numbers: It is possible to create a dodecahedron by attaching two
731 * pentagons to each face of of a cube. The coordinates of the points are:
732 * (+-x,0, z); (+-1, 1, 1); (0, z, x )
733 * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
734 * x = 0.61803398875 and z = 1.61803398875.
736 #define DODECAHEDRON_NUM_VERT 20
737 #define DODECAHEDRON_NUM_FACES 12
738 #define DODECAHEDRON_NUM_EDGE_PER_FACE 5
739 #define DODECAHEDRON_VERT_PER_OBJ (DODECAHEDRON_NUM_FACES*DODECAHEDRON_NUM_EDGE_PER_FACE)
740 #define DODECAHEDRON_VERT_ELEM_PER_OBJ (DODECAHEDRON_VERT_PER_OBJ*3)
741 #define DODECAHEDRON_VERT_PER_OBJ_TRI (DODECAHEDRON_VERT_PER_OBJ+DODECAHEDRON_NUM_FACES*4) /* 4 extra edges per face when drawing pentagons as triangles */
742 /* Vertex Coordinates */
743 static GLfloat dodecahedron_v[DODECAHEDRON_NUM_VERT*3] =
745 0.0f, 1.61803398875f, 0.61803398875f,
747 -0.61803398875f, 0.0f, 1.61803398875f,
748 0.61803398875f, 0.0f, 1.61803398875f,
750 0.0f, 1.61803398875f, -0.61803398875f,
752 0.61803398875f, 0.0f, -1.61803398875f,
753 -0.61803398875f, 0.0f, -1.61803398875f,
754 - 1.0f, 1.0f, - 1.0f,
755 0.0f, -1.61803398875f, 0.61803398875f,
757 - 1.0f, - 1.0f, 1.0f,
758 0.0f, -1.61803398875f, -0.61803398875f,
759 - 1.0f, - 1.0f, - 1.0f,
760 1.0f, - 1.0f, - 1.0f,
761 1.61803398875f, -0.61803398875f, 0.0f,
762 1.61803398875f, 0.61803398875f, 0.0f,
763 -1.61803398875f, 0.61803398875f, 0.0f,
764 -1.61803398875f, -0.61803398875f, 0.0f
767 static GLfloat dodecahedron_n[DODECAHEDRON_NUM_FACES*3] =
769 0.0f, 0.525731112119f, 0.850650808354f,
770 0.0f, 0.525731112119f, -0.850650808354f,
771 0.0f, -0.525731112119f, 0.850650808354f,
772 0.0f, -0.525731112119f, -0.850650808354f,
774 0.850650808354f, 0.0f, 0.525731112119f,
775 -0.850650808354f, 0.0f, 0.525731112119f,
776 0.850650808354f, 0.0f, -0.525731112119f,
777 -0.850650808354f, 0.0f, -0.525731112119f,
779 0.525731112119f, 0.850650808354f, 0.0f,
780 0.525731112119f, -0.850650808354f, 0.0f,
781 -0.525731112119f, 0.850650808354f, 0.0f,
782 -0.525731112119f, -0.850650808354f, 0.0f,
786 static GLubyte dodecahedron_vi[DODECAHEDRON_VERT_PER_OBJ] =
803 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
806 /* -- Icosahedron -- */
807 #define ICOSAHEDRON_NUM_VERT 12
808 #define ICOSAHEDRON_NUM_FACES 20
809 #define ICOSAHEDRON_NUM_EDGE_PER_FACE 3
810 #define ICOSAHEDRON_VERT_PER_OBJ (ICOSAHEDRON_NUM_FACES*ICOSAHEDRON_NUM_EDGE_PER_FACE)
811 #define ICOSAHEDRON_VERT_ELEM_PER_OBJ (ICOSAHEDRON_VERT_PER_OBJ*3)
812 #define ICOSAHEDRON_VERT_PER_OBJ_TRI ICOSAHEDRON_VERT_PER_OBJ
813 /* Vertex Coordinates */
814 static GLfloat icosahedron_v[ICOSAHEDRON_NUM_VERT*3] =
817 0.447213595500f, 0.894427191000f, 0.0f,
818 0.447213595500f, 0.276393202252f, 0.850650808354f,
819 0.447213595500f, -0.723606797748f, 0.525731112119f,
820 0.447213595500f, -0.723606797748f, -0.525731112119f,
821 0.447213595500f, 0.276393202252f, -0.850650808354f,
822 -0.447213595500f, -0.894427191000f, 0.0f,
823 -0.447213595500f, -0.276393202252f, 0.850650808354f,
824 -0.447213595500f, 0.723606797748f, 0.525731112119f,
825 -0.447213595500f, 0.723606797748f, -0.525731112119f,
826 -0.447213595500f, -0.276393202252f, -0.850650808354f,
830 * 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] ) ;
831 * 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] ) ;
832 * 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] ) ;
834 static GLfloat icosahedron_n[ICOSAHEDRON_NUM_FACES*3] =
836 0.760845213037948f, 0.470228201835026f, 0.341640786498800f,
837 0.760845213036861f, -0.179611190632978f, 0.552786404500000f,
838 0.760845213033849f, -0.581234022404097f, 0.0f,
839 0.760845213036861f, -0.179611190632978f, -0.552786404500000f,
840 0.760845213037948f, 0.470228201835026f, -0.341640786498800f,
841 0.179611190628666f, 0.760845213037948f, 0.552786404498399f,
842 0.179611190634277f, -0.290617011204044f, 0.894427191000000f,
843 0.179611190633958f, -0.940456403667806f, 0.0f,
844 0.179611190634278f, -0.290617011204044f, -0.894427191000000f,
845 0.179611190628666f, 0.760845213037948f, -0.552786404498399f,
846 -0.179611190633958f, 0.940456403667806f, 0.0f,
847 -0.179611190634277f, 0.290617011204044f, 0.894427191000000f,
848 -0.179611190628666f, -0.760845213037948f, 0.552786404498399f,
849 -0.179611190628666f, -0.760845213037948f, -0.552786404498399f,
850 -0.179611190634277f, 0.290617011204044f, -0.894427191000000f,
851 -0.760845213036861f, 0.179611190632978f, -0.552786404500000f,
852 -0.760845213033849f, 0.581234022404097f, 0.0f,
853 -0.760845213036861f, 0.179611190632978f, 0.552786404500000f,
854 -0.760845213037948f, -0.470228201835026f, 0.341640786498800f,
855 -0.760845213037948f, -0.470228201835026f, -0.341640786498800f,
859 static GLubyte icosahedron_vi[ICOSAHEDRON_VERT_PER_OBJ] =
882 DECLARE_SHAPE_CACHE(icosahedron,Icosahedron,ICOSAHEDRON)
884 /* -- Octahedron -- */
885 #define OCTAHEDRON_NUM_VERT 6
886 #define OCTAHEDRON_NUM_FACES 8
887 #define OCTAHEDRON_NUM_EDGE_PER_FACE 3
888 #define OCTAHEDRON_VERT_PER_OBJ (OCTAHEDRON_NUM_FACES*OCTAHEDRON_NUM_EDGE_PER_FACE)
889 #define OCTAHEDRON_VERT_ELEM_PER_OBJ (OCTAHEDRON_VERT_PER_OBJ*3)
890 #define OCTAHEDRON_VERT_PER_OBJ_TRI OCTAHEDRON_VERT_PER_OBJ
892 /* Vertex Coordinates */
893 static GLfloat octahedron_v[OCTAHEDRON_NUM_VERT*3] =
904 static GLfloat octahedron_n[OCTAHEDRON_NUM_FACES*3] =
906 0.577350269189f, 0.577350269189f, 0.577350269189f, /* sqrt(1/3) */
907 0.577350269189f, 0.577350269189f,-0.577350269189f,
908 0.577350269189f,-0.577350269189f, 0.577350269189f,
909 0.577350269189f,-0.577350269189f,-0.577350269189f,
910 -0.577350269189f, 0.577350269189f, 0.577350269189f,
911 -0.577350269189f, 0.577350269189f,-0.577350269189f,
912 -0.577350269189f,-0.577350269189f, 0.577350269189f,
913 -0.577350269189f,-0.577350269189f,-0.577350269189f
918 static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OBJ] =
929 DECLARE_SHAPE_CACHE(octahedron,Octahedron,OCTAHEDRON)
931 /* -- RhombicDodecahedron -- */
932 #define RHOMBICDODECAHEDRON_NUM_VERT 14
933 #define RHOMBICDODECAHEDRON_NUM_FACES 12
934 #define RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE 4
935 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ (RHOMBICDODECAHEDRON_NUM_FACES*RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE)
936 #define RHOMBICDODECAHEDRON_VERT_ELEM_PER_OBJ (RHOMBICDODECAHEDRON_VERT_PER_OBJ*3)
937 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ_TRI (RHOMBICDODECAHEDRON_VERT_PER_OBJ+RHOMBICDODECAHEDRON_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
939 /* Vertex Coordinates */
940 static GLfloat rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] =
943 0.707106781187f, 0.0f, 0.5f,
944 0.0f, 0.707106781187f, 0.5f,
945 -0.707106781187f, 0.0f, 0.5f,
946 0.0f, -0.707106781187f, 0.5f,
947 0.707106781187f, 0.707106781187f, 0.0f,
948 -0.707106781187f, 0.707106781187f, 0.0f,
949 -0.707106781187f, -0.707106781187f, 0.0f,
950 0.707106781187f, -0.707106781187f, 0.0f,
951 0.707106781187f, 0.0f, -0.5f,
952 0.0f, 0.707106781187f, -0.5f,
953 -0.707106781187f, 0.0f, -0.5f,
954 0.0f, -0.707106781187f, -0.5f,
958 static GLfloat rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] =
960 0.353553390594f, 0.353553390594f, 0.5f,
961 -0.353553390594f, 0.353553390594f, 0.5f,
962 -0.353553390594f, -0.353553390594f, 0.5f,
963 0.353553390594f, -0.353553390594f, 0.5f,
968 0.353553390594f, 0.353553390594f, -0.5f,
969 -0.353553390594f, 0.353553390594f, -0.5f,
970 -0.353553390594f, -0.353553390594f, -0.5f,
971 0.353553390594f, -0.353553390594f, -0.5f
975 static GLubyte rhombicdodecahedron_vi[RHOMBICDODECAHEDRON_VERT_PER_OBJ] =
990 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
992 /* -- Tetrahedron -- */
993 /* Magic Numbers: r0 = ( 1, 0, 0 )
994 * r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
995 * r2 = ( -1/3, - sqrt(2) / 3, sqrt(6) / 3 )
996 * r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
997 * |r0| = |r1| = |r2| = |r3| = 1
998 * Distance between any two points is 2 sqrt(6) / 3
1000 * Normals: The unit normals are simply the negative of the coordinates of the point not on the surface.
1002 #define TETRAHEDRON_NUM_VERT 4
1003 #define TETRAHEDRON_NUM_FACES 4
1004 #define TETRAHEDRON_NUM_EDGE_PER_FACE 3
1005 #define TETRAHEDRON_VERT_PER_OBJ (TETRAHEDRON_NUM_FACES*TETRAHEDRON_NUM_EDGE_PER_FACE)
1006 #define TETRAHEDRON_VERT_ELEM_PER_OBJ (TETRAHEDRON_VERT_PER_OBJ*3)
1007 #define TETRAHEDRON_VERT_PER_OBJ_TRI TETRAHEDRON_VERT_PER_OBJ
1009 /* Vertex Coordinates */
1010 static GLfloat tetrahedron_v[TETRAHEDRON_NUM_VERT*3] =
1013 -0.333333333333f, 0.942809041582f, 0.0f,
1014 -0.333333333333f, -0.471404520791f, 0.816496580928f,
1015 -0.333333333333f, -0.471404520791f, -0.816496580928f
1017 /* Normal Vectors */
1018 static GLfloat tetrahedron_n[TETRAHEDRON_NUM_FACES*3] =
1021 0.333333333333f, -0.942809041582f, 0.0f,
1022 0.333333333333f, 0.471404520791f, -0.816496580928f,
1023 0.333333333333f, 0.471404520791f, 0.816496580928f
1026 /* Vertex indices */
1027 static GLubyte tetrahedron_vi[TETRAHEDRON_VERT_PER_OBJ] =
1034 DECLARE_SHAPE_CACHE(tetrahedron,Tetrahedron,TETRAHEDRON)
1036 /* -- Sierpinski Sponge -- */
1037 static unsigned int ipow (int x, unsigned int y)
1039 /* return y==0? 1: y==1? x: (y%2? x: 1) * ipow(x*x, y/2); */
1048 return (y%2? x: 1) * ipow(x*x, y/2);
1053 static void fghSierpinskiSpongeGenerate ( int numLevels, double offset[3], GLfloat scale, GLfloat* vertices, GLfloat* normals )
1056 if ( numLevels == 0 )
1058 for (i=0; i<TETRAHEDRON_NUM_FACES; i++)
1061 int faceIdxVertIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE;
1062 for (j=0; j<TETRAHEDRON_NUM_EDGE_PER_FACE; j++)
1064 int outIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE*3+j*3;
1065 int vertIdx = tetrahedron_vi[faceIdxVertIdx+j]*3;
1067 vertices[outIdx ] = (GLfloat)offset[0] + scale * tetrahedron_v[vertIdx ];
1068 vertices[outIdx+1] = (GLfloat)offset[1] + scale * tetrahedron_v[vertIdx+1];
1069 vertices[outIdx+2] = (GLfloat)offset[2] + scale * tetrahedron_v[vertIdx+2];
1071 normals [outIdx ] = tetrahedron_n[normIdx ];
1072 normals [outIdx+1] = tetrahedron_n[normIdx+1];
1073 normals [outIdx+2] = tetrahedron_n[normIdx+2];
1077 else if ( numLevels > 0 )
1079 double local_offset[3] ; /* Use a local variable to avoid buildup of roundoff errors */
1080 unsigned int stride = ipow(4,--numLevels)*TETRAHEDRON_VERT_ELEM_PER_OBJ;
1082 for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ )
1085 local_offset[0] = offset[0] + scale * tetrahedron_v[idx ];
1086 local_offset[1] = offset[1] + scale * tetrahedron_v[idx+1];
1087 local_offset[2] = offset[2] + scale * tetrahedron_v[idx+2];
1088 fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride );
1093 /* -- Now the various non-polyhedra (shapes involving circles) -- */
1095 * Compute lookup table of cos and sin values forming a circle
1096 * (or half circle if halfCircle==TRUE)
1099 * It is the responsibility of the caller to free these tables
1100 * The size of the table is (n+1) to form a connected loop
1101 * The last entry is exactly the same as the first
1102 * The sign of n can be flipped to get the reverse loop
1104 static void fghCircleTable(GLfloat **sint, GLfloat **cost, const int n, const GLboolean halfCircle)
1108 /* Table size, the sign of n flips the circle direction */
1109 const int size = abs(n);
1111 /* Determine the angle between samples */
1112 const GLfloat angle = (halfCircle?1:2)*(GLfloat)M_PI/(GLfloat)( ( n == 0 ) ? 1 : n );
1114 /* Allocate memory for n samples, plus duplicate of first entry at the end */
1115 *sint = malloc(sizeof(GLfloat) * (size+1));
1116 *cost = malloc(sizeof(GLfloat) * (size+1));
1118 /* Bail out if memory allocation fails, fgError never returns */
1119 if (!(*sint) || !(*cost))
1123 fgError("Failed to allocate memory in fghCircleTable");
1126 /* Compute cos and sin around the circle */
1130 for (i=1; i<size; i++)
1132 (*sint)[i] = (GLfloat)sin(angle*i);
1133 (*cost)[i] = (GLfloat)cos(angle*i);
1139 (*sint)[size] = 0.0f; /* sin PI */
1140 (*cost)[size] = -1.0f; /* cos PI */
1144 /* Last sample is duplicate of the first (sin or cos of 2 PI) */
1145 (*sint)[size] = (*sint)[0];
1146 (*cost)[size] = (*cost)[0];
1150 static void fghGenerateSphere(GLfloat radius, GLint slices, GLint stacks, GLfloat **vertices, GLfloat **normals, int* nVert)
1153 int idx = 0; /* idx into vertex/normal buffer */
1156 /* Pre-computed circle */
1157 GLfloat *sint1,*cost1;
1158 GLfloat *sint2,*cost2;
1160 /* number of unique vertices */
1161 if (slices==0 || stacks<2)
1163 /* nothing to generate */
1167 *nVert = slices*(stacks-1)+2;
1168 if ((*nVert) > 65535)
1170 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1172 fgWarning("fghGenerateSphere: too many slices or stacks requested, indices will wrap");
1174 /* precompute values on unit circle */
1175 fghCircleTable(&sint1,&cost1,-slices,GL_FALSE);
1176 fghCircleTable(&sint2,&cost2, stacks,GL_TRUE);
1178 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1179 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1180 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1181 if (!(*vertices) || !(*normals))
1185 fgError("Failed to allocate memory in fghGenerateSphere");
1189 (*vertices)[0] = 0.f;
1190 (*vertices)[1] = 0.f;
1191 (*vertices)[2] = radius;
1192 (*normals )[0] = 0.f;
1193 (*normals )[1] = 0.f;
1194 (*normals )[2] = 1.f;
1198 for( i=1; i<stacks; i++ )
1200 for(j=0; j<slices; j++, idx+=3)
1202 x = cost1[j]*sint2[i];
1203 y = sint1[j]*sint2[i];
1206 (*vertices)[idx ] = x*radius;
1207 (*vertices)[idx+1] = y*radius;
1208 (*vertices)[idx+2] = z*radius;
1209 (*normals )[idx ] = x;
1210 (*normals )[idx+1] = y;
1211 (*normals )[idx+2] = z;
1216 (*vertices)[idx ] = 0.f;
1217 (*vertices)[idx+1] = 0.f;
1218 (*vertices)[idx+2] = -radius;
1219 (*normals )[idx ] = 0.f;
1220 (*normals )[idx+1] = 0.f;
1221 (*normals )[idx+2] = -1.f;
1223 /* Done creating vertices, release sin and cos tables */
1230 void fghGenerateCone(
1231 GLfloat base, GLfloat height, GLint slices, GLint stacks, /* input */
1232 GLfloat **vertices, GLfloat **normals, int* nVert /* output */
1236 int idx = 0; /* idx into vertex/normal buffer */
1238 /* Pre-computed circle */
1239 GLfloat *sint,*cost;
1241 /* Step in z and radius as stacks are drawn. */
1243 GLfloat r = (GLfloat)base;
1245 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1246 const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
1248 /* Scaling factors for vertex normals */
1249 const GLfloat cosn = (GLfloat) (height / sqrt( height * height + base * base ));
1250 const GLfloat sinn = (GLfloat) (base / sqrt( height * height + base * base ));
1254 /* number of unique vertices */
1255 if (slices==0 || stacks<1)
1257 /* nothing to generate */
1261 *nVert = slices*(stacks+2)+1; /* need an extra stack for closing off bottom with correct normals */
1263 if ((*nVert) > 65535)
1265 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1267 fgWarning("fghGenerateCone: too many slices or stacks requested, indices will wrap");
1269 /* Pre-computed circle */
1270 fghCircleTable(&sint,&cost,-slices,GL_FALSE);
1272 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1273 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1274 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1275 if (!(*vertices) || !(*normals))
1279 fgError("Failed to allocate memory in fghGenerateCone");
1283 (*vertices)[0] = 0.f;
1284 (*vertices)[1] = 0.f;
1286 (*normals )[0] = 0.f;
1287 (*normals )[1] = 0.f;
1288 (*normals )[2] = -1.f;
1290 /* other on bottom (get normals right) */
1291 for (j=0; j<slices; j++, idx+=3)
1293 (*vertices)[idx ] = cost[j]*r;
1294 (*vertices)[idx+1] = sint[j]*r;
1295 (*vertices)[idx+2] = z;
1296 (*normals )[idx ] = 0.f;
1297 (*normals )[idx+1] = 0.f;
1298 (*normals )[idx+2] = -1.f;
1302 for (i=0; i<stacks+1; i++ )
1304 for (j=0; j<slices; j++, idx+=3)
1306 (*vertices)[idx ] = cost[j]*r;
1307 (*vertices)[idx+1] = sint[j]*r;
1308 (*vertices)[idx+2] = z;
1309 (*normals )[idx ] = cost[j]*cosn;
1310 (*normals )[idx+1] = sint[j]*cosn;
1311 (*normals )[idx+2] = sinn;
1318 /* Release sin and cos tables */
1323 void fghGenerateCylinder(
1324 GLfloat radius, GLfloat height, GLint slices, GLint stacks, /* input */
1325 GLfloat **vertices, GLfloat **normals, int* nVert /* output */
1329 int idx = 0; /* idx into vertex/normal buffer */
1331 /* Step in z as stacks are drawn. */
1332 GLfloat radf = (GLfloat)radius;
1334 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1336 /* Pre-computed circle */
1337 GLfloat *sint,*cost;
1339 /* number of unique vertices */
1340 if (slices==0 || stacks<1)
1342 /* nothing to generate */
1346 *nVert = slices*(stacks+3)+2; /* need two extra stacks for closing off top and bottom with correct normals */
1348 if ((*nVert) > 65535)
1350 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1352 fgWarning("fghGenerateCylinder: too many slices or stacks requested, indices will wrap");
1354 /* Pre-computed circle */
1355 fghCircleTable(&sint,&cost,-slices,GL_FALSE);
1357 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1358 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1359 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1360 if (!(*vertices) || !(*normals))
1364 fgError("Failed to allocate memory in fghGenerateCylinder");
1369 (*vertices)[0] = 0.f;
1370 (*vertices)[1] = 0.f;
1371 (*vertices)[2] = 0.f;
1372 (*normals )[0] = 0.f;
1373 (*normals )[1] = 0.f;
1374 (*normals )[2] = -1.f;
1376 /* other on top (get normals right) */
1377 for (j=0; j<slices; j++, idx+=3)
1379 (*vertices)[idx ] = cost[j]*radf;
1380 (*vertices)[idx+1] = sint[j]*radf;
1381 (*vertices)[idx+2] = z;
1382 (*normals )[idx ] = 0.f;
1383 (*normals )[idx+1] = 0.f;
1384 (*normals )[idx+2] = -1.f;
1388 for (i=0; i<stacks+1; i++ )
1390 for (j=0; j<slices; j++, idx+=3)
1392 (*vertices)[idx ] = cost[j]*radf;
1393 (*vertices)[idx+1] = sint[j]*radf;
1394 (*vertices)[idx+2] = z;
1395 (*normals )[idx ] = cost[j];
1396 (*normals )[idx+1] = sint[j];
1397 (*normals )[idx+2] = 0.f;
1403 /* other on bottom (get normals right) */
1405 for (j=0; j<slices; j++, idx+=3)
1407 (*vertices)[idx ] = cost[j]*radf;
1408 (*vertices)[idx+1] = sint[j]*radf;
1409 (*vertices)[idx+2] = z;
1410 (*normals )[idx ] = 0.f;
1411 (*normals )[idx+1] = 0.f;
1412 (*normals )[idx+2] = 1.f;
1416 (*vertices)[idx ] = 0.f;
1417 (*vertices)[idx+1] = 0.f;
1418 (*vertices)[idx+2] = height;
1419 (*normals )[idx ] = 0.f;
1420 (*normals )[idx+1] = 0.f;
1421 (*normals )[idx+2] = 1.f;
1423 /* Release sin and cos tables */
1428 void fghGenerateTorus(
1429 double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings, /* input */
1430 GLfloat **vertices, GLfloat **normals, int* nVert /* output */
1433 GLfloat iradius = (float)dInnerRadius;
1434 GLfloat oradius = (float)dOuterRadius;
1437 /* Pre-computed circle */
1438 GLfloat *spsi, *cpsi;
1439 GLfloat *sphi, *cphi;
1441 /* number of unique vertices */
1442 if (nSides<2 || nRings<2)
1444 /* nothing to generate */
1448 *nVert = nSides * nRings;
1450 if ((*nVert) > 65535)
1452 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1454 fgWarning("fghGenerateTorus: too many slices or stacks requested, indices will wrap");
1456 /* precompute values on unit circle */
1457 fghCircleTable(&spsi,&cpsi, nRings,GL_FALSE);
1458 fghCircleTable(&sphi,&cphi,-nSides,GL_FALSE);
1460 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1461 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1462 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1463 if (!(*vertices) || !(*normals))
1467 fgError("Failed to allocate memory in fghGenerateTorus");
1470 for( j=0; j<nRings; j++ )
1472 for( i=0; i<nSides; i++ )
1474 int offset = 3 * ( j * nSides + i ) ;
1476 (*vertices)[offset ] = cpsi[j] * ( oradius + cphi[i] * iradius ) ;
1477 (*vertices)[offset+1] = spsi[j] * ( oradius + cphi[i] * iradius ) ;
1478 (*vertices)[offset+2] = sphi[i] * iradius ;
1479 (*normals )[offset ] = cpsi[j] * cphi[i] ;
1480 (*normals )[offset+1] = spsi[j] * cphi[i] ;
1481 (*normals )[offset+2] = sphi[i] ;
1485 /* Release sin and cos tables */
1492 /* -- INTERNAL DRAWING functions --------------------------------------- */
1493 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
1494 static void fgh##nameICaps( GLboolean useWireMode )\
1498 fgh##nameICaps##Generate();\
1499 name##Cached = GL_TRUE;\
1504 fghDrawGeometryWire (name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ, \
1505 NULL,nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE,GL_LINE_LOOP,\
1510 fghDrawGeometrySolid(name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ,\
1511 vertIdxs, 1, nameCaps##_VERT_PER_OBJ_TRI); \
1514 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
1515 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
1517 static void fghCube( GLfloat dSize, GLboolean useWireMode )
1524 cubeCached = GL_TRUE;
1529 /* Need to build new vertex list containing vertices for cube of different size */
1532 vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
1534 /* Bail out if memory allocation fails, fgError never returns */
1538 fgError("Failed to allocate memory in fghCube");
1541 for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
1542 vertices[i] = dSize*cube_verts[i];
1545 vertices = cube_verts;
1548 fghDrawGeometryWire(vertices, cube_norms, CUBE_VERT_PER_OBJ,
1549 NULL,CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1552 fghDrawGeometrySolid(vertices, cube_norms, CUBE_VERT_PER_OBJ,
1553 cube_vertIdxs, 1, CUBE_VERT_PER_OBJ_TRI);
1556 /* cleanup allocated memory */
1560 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
1561 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON)
1562 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON)
1563 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
1564 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON)
1566 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
1570 GLsizei numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
1571 GLsizei numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
1572 GLsizei numFace = numTetr*TETRAHEDRON_NUM_FACES;
1576 /* Allocate memory */
1577 vertices = malloc(numVert*3 * sizeof(GLfloat));
1578 normals = malloc(numVert*3 * sizeof(GLfloat));
1579 /* Bail out if memory allocation fails, fgError never returns */
1580 if (!vertices || !normals)
1584 fgError("Failed to allocate memory in fghSierpinskiSponge");
1587 /* Generate elements */
1588 fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
1590 /* Draw and cleanup */
1592 fghDrawGeometryWire (vertices,normals,numVert,
1593 NULL,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1596 fghDrawGeometrySolid(vertices,normals,numVert,NULL,1,0);
1604 static void fghSphere( GLfloat radius, GLint slices, GLint stacks, GLboolean useWireMode )
1607 GLfloat *vertices, *normals;
1609 /* Generate vertices and normals */
1610 fghGenerateSphere(radius,slices,stacks,&vertices,&normals,&nVert);
1613 /* nothing to draw */
1618 GLushort *sliceIdx, *stackIdx;
1619 /* First, generate vertex index arrays for drawing with glDrawElements
1620 * We have a bunch of line_loops to draw for each stack, and a
1621 * bunch for each slice.
1624 sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1625 stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort));
1626 if (!(stackIdx) || !(sliceIdx))
1630 fgError("Failed to allocate memory in fghSphere");
1633 /* generate for each stack */
1634 for (i=0,idx=0; i<stacks-1; i++)
1636 GLushort offset = 1+i*slices; /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1637 for (j=0; j<slices; j++, idx++)
1639 stackIdx[idx] = offset+j;
1643 /* generate for each slice */
1644 for (i=0,idx=0; i<slices; i++)
1646 GLushort offset = 1+i; /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1647 sliceIdx[idx++] = 0; /* vertex on top */
1648 for (j=0; j<stacks-1; j++, idx++)
1650 sliceIdx[idx] = offset+j*slices;
1652 sliceIdx[idx++] = nVert-1; /* zero based index, last element in array... */
1656 fghDrawGeometryWire(vertices,normals,nVert,
1657 sliceIdx,slices,stacks+1,GL_LINE_STRIP,
1658 stackIdx,stacks-1,slices);
1660 /* cleanup allocated memory */
1666 /* First, generate vertex index arrays for drawing with glDrawElements
1667 * All stacks, including top and bottom are covered with a triangle
1671 /* Create index vector */
1674 /* Allocate buffers for indices, bail out if memory allocation fails */
1675 stripIdx = malloc((slices+1)*2*(stacks)*sizeof(GLushort));
1679 fgError("Failed to allocate memory in fghSphere");
1683 for (j=0, idx=0; j<slices; j++, idx+=2)
1685 stripIdx[idx ] = j+1; /* 0 is top vertex, 1 is first for first stack */
1686 stripIdx[idx+1] = 0;
1688 stripIdx[idx ] = 1; /* repeat first slice's idx for closing off shape */
1689 stripIdx[idx+1] = 0;
1692 /* middle stacks: */
1693 /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1694 for (i=0; i<stacks-2; i++, idx+=2)
1696 offset = 1+i*slices; /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1697 for (j=0; j<slices; j++, idx+=2)
1699 stripIdx[idx ] = offset+j+slices;
1700 stripIdx[idx+1] = offset+j;
1702 stripIdx[idx ] = offset+slices; /* repeat first slice's idx for closing off shape */
1703 stripIdx[idx+1] = offset;
1707 offset = 1+(stacks-2)*slices; /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1708 for (j=0; j<slices; j++, idx+=2)
1710 stripIdx[idx ] = nVert-1; /* zero based index, last element in array (bottom vertex)... */
1711 stripIdx[idx+1] = offset+j;
1713 stripIdx[idx ] = nVert-1; /* repeat first slice's idx for closing off shape */
1714 stripIdx[idx+1] = offset;
1718 fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks,(slices+1)*2);
1720 /* cleanup allocated memory */
1724 /* cleanup allocated memory */
1729 static void fghCone( GLfloat base, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1732 GLfloat *vertices, *normals;
1734 /* Generate vertices and normals */
1735 /* Note, (stacks+1)*slices vertices for side of object, slices+1 for top and bottom closures */
1736 fghGenerateCone(base,height,slices,stacks,&vertices,&normals,&nVert);
1739 /* nothing to draw */
1744 GLushort *sliceIdx, *stackIdx;
1745 /* First, generate vertex index arrays for drawing with glDrawElements
1746 * We have a bunch of line_loops to draw for each stack, and a
1747 * bunch for each slice.
1750 stackIdx = malloc(slices*stacks*sizeof(GLushort));
1751 sliceIdx = malloc(slices*2 *sizeof(GLushort));
1752 if (!(stackIdx) || !(sliceIdx))
1756 fgError("Failed to allocate memory in fghCone");
1759 /* generate for each stack */
1760 for (i=0,idx=0; i<stacks; i++)
1762 GLushort offset = 1+(i+1)*slices; /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1763 for (j=0; j<slices; j++, idx++)
1765 stackIdx[idx] = offset+j;
1769 /* generate for each slice */
1770 for (i=0,idx=0; i<slices; i++)
1772 GLushort offset = 1+i; /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1773 sliceIdx[idx++] = offset+slices;
1774 sliceIdx[idx++] = offset+(stacks+1)*slices;
1778 fghDrawGeometryWire(vertices,normals,nVert,
1779 sliceIdx,1,slices*2,GL_LINES,
1780 stackIdx,stacks,slices);
1782 /* cleanup allocated memory */
1788 /* First, generate vertex index arrays for drawing with glDrawElements
1789 * All stacks, including top and bottom are covered with a triangle
1793 /* Create index vector */
1796 /* Allocate buffers for indices, bail out if memory allocation fails */
1797 stripIdx = malloc((slices+1)*2*(stacks+1)*sizeof(GLushort)); /*stacks +1 because of closing off bottom */
1801 fgError("Failed to allocate memory in fghCone");
1805 for (j=0, idx=0; j<slices; j++, idx+=2)
1808 stripIdx[idx+1] = j+1; /* 0 is top vertex, 1 is first for first stack */
1810 stripIdx[idx ] = 0; /* repeat first slice's idx for closing off shape */
1811 stripIdx[idx+1] = 1;
1814 /* middle stacks: */
1815 /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1816 for (i=0; i<stacks; i++, idx+=2)
1818 offset = 1+(i+1)*slices; /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1819 for (j=0; j<slices; j++, idx+=2)
1821 stripIdx[idx ] = offset+j;
1822 stripIdx[idx+1] = offset+j+slices;
1824 stripIdx[idx ] = offset; /* repeat first slice's idx for closing off shape */
1825 stripIdx[idx+1] = offset+slices;
1829 fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks+1,(slices+1)*2);
1831 /* cleanup allocated memory */
1835 /* cleanup allocated memory */
1840 static void fghCylinder( GLfloat radius, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1843 GLfloat *vertices, *normals;
1845 /* Generate vertices and normals */
1846 /* Note, (stacks+1)*slices vertices for side of object, 2*slices+2 for top and bottom closures */
1847 fghGenerateCylinder(radius,height,slices,stacks,&vertices,&normals,&nVert);
1850 /* nothing to draw */
1855 GLushort *sliceIdx, *stackIdx;
1856 /* First, generate vertex index arrays for drawing with glDrawElements
1857 * We have a bunch of line_loops to draw for each stack, and a
1858 * bunch for each slice.
1861 stackIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1862 sliceIdx = malloc(slices*2 *sizeof(GLushort));
1863 if (!(stackIdx) || !(sliceIdx))
1867 fgError("Failed to allocate memory in fghCylinder");
1870 /* generate for each stack */
1871 for (i=0,idx=0; i<stacks+1; i++)
1873 GLushort offset = 1+(i+1)*slices; /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1874 for (j=0; j<slices; j++, idx++)
1876 stackIdx[idx] = offset+j;
1880 /* generate for each slice */
1881 for (i=0,idx=0; i<slices; i++)
1883 GLushort offset = 1+i; /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1884 sliceIdx[idx++] = offset+slices;
1885 sliceIdx[idx++] = offset+(stacks+1)*slices;
1889 fghDrawGeometryWire(vertices,normals,nVert,
1890 sliceIdx,1,slices*2,GL_LINES,
1891 stackIdx,stacks+1,slices);
1893 /* cleanup allocated memory */
1899 /* First, generate vertex index arrays for drawing with glDrawElements
1900 * All stacks, including top and bottom are covered with a triangle
1904 /* Create index vector */
1907 /* Allocate buffers for indices, bail out if memory allocation fails */
1908 stripIdx = malloc((slices+1)*2*(stacks+2)*sizeof(GLushort)); /*stacks +2 because of closing off bottom and top */
1912 fgError("Failed to allocate memory in fghCylinder");
1916 for (j=0, idx=0; j<slices; j++, idx+=2)
1919 stripIdx[idx+1] = j+1; /* 0 is top vertex, 1 is first for first stack */
1921 stripIdx[idx ] = 0; /* repeat first slice's idx for closing off shape */
1922 stripIdx[idx+1] = 1;
1925 /* middle stacks: */
1926 /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1927 for (i=0; i<stacks; i++, idx+=2)
1929 offset = 1+(i+1)*slices; /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1930 for (j=0; j<slices; j++, idx+=2)
1932 stripIdx[idx ] = offset+j;
1933 stripIdx[idx+1] = offset+j+slices;
1935 stripIdx[idx ] = offset; /* repeat first slice's idx for closing off shape */
1936 stripIdx[idx+1] = offset+slices;
1940 offset = 1+(stacks+2)*slices;
1941 for (j=0; j<slices; j++, idx+=2)
1943 stripIdx[idx ] = offset+j;
1944 stripIdx[idx+1] = nVert-1; /* zero based index, last element in array (bottom vertex)... */
1946 stripIdx[idx ] = offset;
1947 stripIdx[idx+1] = nVert-1; /* repeat first slice's idx for closing off shape */
1950 fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,stacks+2,(slices+1)*2);
1952 /* cleanup allocated memory */
1956 /* cleanup allocated memory */
1961 static void fghTorus( GLfloat dInnerRadius, GLfloat dOuterRadius, GLint nSides, GLint nRings, GLboolean useWireMode )
1964 GLfloat *vertices, *normals;
1966 /* Generate vertices and normals */
1967 fghGenerateTorus(dInnerRadius,dOuterRadius,nSides,nRings, &vertices,&normals,&nVert);
1970 /* nothing to draw */
1975 GLushort *sideIdx, *ringIdx;
1976 /* First, generate vertex index arrays for drawing with glDrawElements
1977 * We have a bunch of line_loops to draw each side, and a
1978 * bunch for each ring.
1981 ringIdx = malloc(nRings*nSides*sizeof(GLushort));
1982 sideIdx = malloc(nSides*nRings*sizeof(GLushort));
1983 if (!(ringIdx) || !(sideIdx))
1987 fgError("Failed to allocate memory in fghTorus");
1990 /* generate for each ring */
1991 for( j=0,idx=0; j<nRings; j++ )
1992 for( i=0; i<nSides; i++, idx++ )
1993 ringIdx[idx] = j * nSides + i;
1995 /* generate for each side */
1996 for( i=0,idx=0; i<nSides; i++ )
1997 for( j=0; j<nRings; j++, idx++ )
1998 sideIdx[idx] = j * nSides + i;
2001 fghDrawGeometryWire(vertices,normals,nVert,
2002 ringIdx,nRings,nSides,GL_LINE_LOOP,
2003 sideIdx,nSides,nRings);
2005 /* cleanup allocated memory */
2011 /* First, generate vertex index arrays for drawing with glDrawElements
2012 * All stacks, including top and bottom are covered with a triangle
2017 /* Allocate buffers for indices, bail out if memory allocation fails */
2018 stripIdx = malloc((nRings+1)*2*nSides*sizeof(GLushort));
2022 fgError("Failed to allocate memory in fghTorus");
2025 for( i=0, idx=0; i<nSides; i++ )
2031 for( j=0; j<nRings; j++, idx+=2 )
2033 int offset = j * nSides + i;
2034 stripIdx[idx ] = offset;
2035 stripIdx[idx+1] = offset + ioff;
2037 /* repeat first to close off shape */
2039 stripIdx[idx+1] = i + ioff;
2044 fghDrawGeometrySolid(vertices,normals,nVert,stripIdx,nSides,(nRings+1)*2);
2046 /* cleanup allocated memory */
2050 /* cleanup allocated memory */
2056 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
2060 * Draws a solid sphere
2062 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
2064 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
2065 fghSphere((GLfloat)radius, slices, stacks, GL_FALSE );
2069 * Draws a wire sphere
2071 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
2073 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
2074 fghSphere((GLfloat)radius, slices, stacks, GL_TRUE );
2079 * Draws a solid cone
2081 void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks )
2083 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
2084 fghCone((GLfloat)base, (GLfloat)height, slices, stacks, GL_FALSE );
2090 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
2092 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
2093 fghCone((GLfloat)base, (GLfloat)height, slices, stacks, GL_TRUE );
2098 * Draws a solid cylinder
2100 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
2102 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
2103 fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, GL_FALSE );
2107 * Draws a wire cylinder
2109 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
2111 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
2112 fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, GL_TRUE );
2116 * Draws a wire torus
2118 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
2120 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
2121 fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, GL_TRUE);
2125 * Draws a solid torus
2127 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
2129 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
2130 fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, GL_FALSE);
2135 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
2136 /* Macro to generate interface functions */
2137 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
2138 void FGAPIENTRY glutWire##nameICaps( void )\
2140 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
2141 fgh##nameICaps( GL_TRUE );\
2143 void FGAPIENTRY glutSolid##nameICaps( void )\
2145 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
2146 fgh##nameICaps( GL_FALSE );\
2149 void FGAPIENTRY glutWireCube( double dSize )
2151 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
2152 fghCube( (GLfloat)dSize, GL_TRUE );
2154 void FGAPIENTRY glutSolidCube( double dSize )
2156 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
2157 fghCube( (GLfloat)dSize, GL_FALSE );
2160 DECLARE_SHAPE_INTERFACE(Dodecahedron)
2161 DECLARE_SHAPE_INTERFACE(Icosahedron)
2162 DECLARE_SHAPE_INTERFACE(Octahedron)
2163 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron)
2165 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
2167 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
2168 fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, GL_TRUE );
2170 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
2172 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
2173 fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, GL_FALSE );
2176 DECLARE_SHAPE_INTERFACE(Tetrahedron)
2179 /*** END OF FILE ***/