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 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
48 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
49 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
51 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
52 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart);
53 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
54 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
55 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
56 GLint attribute_v_coord, GLint attribute_v_normal
58 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
59 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
60 GLint attribute_v_coord, GLint attribute_v_normal, GLint attribute_v_texture);
61 /* declare function for generating visualization of normals */
62 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices);
63 static void fghDrawNormalVisualization11();
64 static void fghDrawNormalVisualization20(GLint attribute_v_coord);
67 * Explanation of the functions has to be separate for the polyhedra and
68 * the non-polyhedra (objects with a circular cross-section).
70 * - We have only implemented the five platonic solids and the rhomboid
71 * dodecahedron. If you need more types of polyhedra, please see
73 * - Solids are drawn by glDrawArrays if composed of triangular faces
74 * (the tetrahedron, octahedron, and icosahedron), or are first
75 * decomposed into triangles and then drawn by glDrawElements if its
76 * faces are squares or pentagons (cube, dodecahedron and rhombic
77 * dodecahedron) as some vertices are repeated in that case.
78 * - WireFrame drawing is done using a GL_LINE_LOOP per face, and thus
79 * issuing one draw call per face. glDrawArrays is always used as no
80 * triangle decomposition is needed to draw faces. We use the "first"
81 * parameter in glDrawArrays to go from face to face.
84 * - We have implemented the sphere, cylinder, cone and torus.
85 * - All shapes are characterized by two parameters: the number of
86 * subdivisions along two axes used to construct the shape's vertices
87 * (e.g. stacks and slices for the sphere).
88 * As different subdivisions are most suitable for different shapes,
89 * and are thus also named differently, I wont provide general comments
91 * - Solids are drawn using glDrawArrays and GL_TRIANGLE_STRIP. Each
92 * strip covers one revolution around one of the two subdivision axes
94 * - WireFrame drawing is done for the subdivisions along the two axes
95 * separately, usually using GL_LINE_LOOP. Vertex index arrays are
96 * built containing the vertices to be drawn for each loop, which are
97 * then drawn using multiple calls to glDrawElements. As the number of
98 * subdivisions along the two axes is not guaranteed to be equal, the
99 * vertex indices for e.g. stacks and slices are stored in separate
100 * arrays, which makes the input to the drawing function a bit clunky,
101 * but allows for the same drawing function to be used for all shapes.
106 * Draw geometric shape in wire mode (only edges)
109 * GLfloat *vertices, GLfloat *normals, GLsizei numVertices
110 * The vertex coordinate and normal buffers, and the number of entries in
113 * a vertex indices buffer, optional (never passed for the polyhedra)
114 * GLsizei numParts, GLsizei numVertPerPart
115 * polyhedra: number of faces, and the number of vertices for drawing
117 * non-polyhedra: number of edges to draw for first subdivision (not
118 * necessarily equal to number of subdivisions requested by user, e.g.
119 * as each subdivision is enclosed by two edges), and number of
120 * vertices for drawing each
121 * numParts * numVertPerPart gives the number of entries in the vertex
124 * vertex drawing mode (e.g. always GL_LINE_LOOP for polyhedra, varies
126 * GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
127 * non-polyhedra only: same as the above, but now for subdivisions along
128 * the other axis. Always drawn as GL_LINE_LOOP.
130 * Feel free to contribute better naming ;)
132 void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
133 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
134 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
137 SFG_Window *win = fgStructure.CurrentWindow;
139 GLint attribute_v_coord = win ? win->Window.attribute_v_coord : -1;
140 GLint attribute_v_normal = win ? win->Window.attribute_v_normal : -1;
142 if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
143 /* User requested a 2.0 draw */
144 fghDrawGeometryWire20(vertices, normals, numVertices,
145 vertIdxs, numParts, numVertPerPart, vertexMode,
146 vertIdxs2, numParts2, numVertPerPart2,
147 attribute_v_coord, attribute_v_normal);
149 fghDrawGeometryWire11(vertices, normals,
150 vertIdxs, numParts, numVertPerPart, vertexMode,
151 vertIdxs2, numParts2, numVertPerPart2);
154 /* Draw the geometric shape with filled triangles
157 * GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices
158 * The vertex coordinate, normal and texture coordinate buffers, and the
159 * number of entries in those
161 * a vertex indices buffer, optional (not passed for the polyhedra with
163 * GLsizei numParts, GLsizei numVertPerPart
164 * polyhedra: not used for polyhedra with triangular faces
165 (numEdgePerFace==3), as each vertex+normal pair is drawn only once,
166 so no vertex indices are used.
167 Else, the shape was triangulated (DECOMPOSE_TO_TRIANGLE), leading to
168 reuse of some vertex+normal pairs, and thus the need to draw with
169 glDrawElements. numParts is always 1 in this case (we can draw the
170 whole object with one call to glDrawElements as the vertex index
171 array contains separate triangles), and numVertPerPart indicates
172 the number of vertex indices in the vertex array.
173 * non-polyhedra: number of parts (GL_TRIANGLE_STRIPs) to be drawn
174 separately (numParts calls to glDrawElements) to create the object.
175 numVertPerPart indicates the number of vertex indices to be
176 processed at each draw call.
177 * numParts * numVertPerPart gives the number of entries in the vertex
180 void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
181 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
183 GLint attribute_v_coord, attribute_v_normal, attribute_v_texture;
184 SFG_Window *win = fgStructure.CurrentWindow;
186 attribute_v_coord = win->Window.attribute_v_coord;
187 attribute_v_normal = win->Window.attribute_v_normal;
188 attribute_v_texture = win->Window.attribute_v_texture;
190 attribute_v_coord = attribute_v_normal = attribute_v_texture = -1;
193 if (win && win->State.VisualizeNormals)
194 /* generate normals for each vertex to be drawn as well */
195 fghGenerateNormalVisualization(vertices, normals, numVertices);
197 if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1))
199 /* User requested a 2.0 draw */
200 fghDrawGeometrySolid20(vertices, normals, textcs, numVertices,
201 vertIdxs, numParts, numVertIdxsPerPart,
202 attribute_v_coord, attribute_v_normal, attribute_v_texture);
204 if (win && win->State.VisualizeNormals)
205 /* draw normals for each vertex as well */
206 fghDrawNormalVisualization20(attribute_v_coord);
210 fghDrawGeometrySolid11(vertices, normals, textcs, numVertices,
211 vertIdxs, numParts, numVertIdxsPerPart);
213 if (win && win->State.VisualizeNormals)
214 /* draw normals for each vertex as well */
215 fghDrawNormalVisualization11();
221 /* Version for OpenGL (ES) 1.1 */
222 static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals,
223 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
224 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2
229 glEnableClientState(GL_VERTEX_ARRAY);
230 glEnableClientState(GL_NORMAL_ARRAY);
232 glVertexPointer(3, GL_FLOAT, 0, vertices);
233 glNormalPointer(GL_FLOAT, 0, normals);
237 /* Draw per face (TODO: could use glMultiDrawArrays if available) */
238 for (i=0; i<numParts; i++)
239 glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
241 for (i=0; i<numParts; i++)
242 glDrawElements(vertexMode,numVertPerPart,GL_UNSIGNED_SHORT,vertIdxs+i*numVertPerPart);
245 for (i=0; i<numParts2; i++)
246 glDrawElements(GL_LINE_LOOP,numVertPerPart2,GL_UNSIGNED_SHORT,vertIdxs2+i*numVertPerPart2);
248 glDisableClientState(GL_VERTEX_ARRAY);
249 glDisableClientState(GL_NORMAL_ARRAY);
253 static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
254 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart)
258 glEnableClientState(GL_VERTEX_ARRAY);
259 glEnableClientState(GL_NORMAL_ARRAY);
261 glVertexPointer(3, GL_FLOAT, 0, vertices);
262 glNormalPointer(GL_FLOAT, 0, normals);
266 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
267 glTexCoordPointer(2, GL_FLOAT, 0, textcs);
271 glDrawArrays(GL_TRIANGLES, 0, numVertices);
274 for (i=0; i<numParts; i++)
275 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs+i*numVertIdxsPerPart);
277 glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs);
279 glDisableClientState(GL_VERTEX_ARRAY);
280 glDisableClientState(GL_NORMAL_ARRAY);
282 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
285 /* Version for OpenGL (ES) >= 2.0 */
286 static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices,
287 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode,
288 GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2,
289 GLint attribute_v_coord, GLint attribute_v_normal)
291 GLuint vbo_coords = 0, vbo_normals = 0,
292 ibo_elements = 0, ibo_elements2 = 0;
293 GLsizei numVertIdxs = numParts * numVertPerPart;
294 GLsizei numVertIdxs2 = numParts2 * numVertPerPart2;
297 if (numVertices > 0 && attribute_v_coord != -1) {
298 fghGenBuffers(1, &vbo_coords);
299 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
300 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
301 vertices, FGH_STATIC_DRAW);
304 if (numVertices > 0 && attribute_v_normal != -1) {
305 fghGenBuffers(1, &vbo_normals);
306 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
307 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
308 normals, FGH_STATIC_DRAW);
311 if (vertIdxs != NULL) {
312 fghGenBuffers(1, &ibo_elements);
313 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
314 fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
315 vertIdxs, FGH_STATIC_DRAW);
316 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
319 if (vertIdxs2 != NULL) {
320 fghGenBuffers(1, &ibo_elements2);
321 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
322 fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs2 * sizeof(vertIdxs2[0]),
323 vertIdxs2, FGH_STATIC_DRAW);
324 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
328 fghEnableVertexAttribArray(attribute_v_coord);
329 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
330 fghVertexAttribPointer(
331 attribute_v_coord, /* attribute */
332 3, /* number of elements per vertex, here (x,y,z) */
333 GL_FLOAT, /* the type of each element */
334 GL_FALSE, /* take our values as-is */
335 0, /* no extra data between each position */
336 0 /* offset of first element */
338 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
342 fghEnableVertexAttribArray(attribute_v_normal);
343 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
344 fghVertexAttribPointer(
345 attribute_v_normal, /* attribute */
346 3, /* number of elements per vertex, here (x,y,z) */
347 GL_FLOAT, /* the type of each element */
348 GL_FALSE, /* take our values as-is */
349 0, /* no extra data between each position */
350 0 /* offset of first element */
352 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
356 /* Draw per face (TODO: could use glMultiDrawArrays if available) */
357 for (i=0; i<numParts; i++)
358 glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart);
360 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
361 for (i=0; i<numParts; i++)
362 glDrawElements(vertexMode, numVertPerPart,
363 GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertPerPart));
364 /* Clean existing bindings before clean-up */
365 /* Android showed instability otherwise */
366 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
370 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2);
371 for (i=0; i<numParts2; i++)
372 glDrawElements(GL_LINE_LOOP, numVertPerPart2,
373 GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs2[0])*i*numVertPerPart2));
374 /* Clean existing bindings before clean-up */
375 /* Android showed instability otherwise */
376 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
380 fghDisableVertexAttribArray(attribute_v_coord);
381 if (vbo_normals != 0)
382 fghDisableVertexAttribArray(attribute_v_normal);
385 fghDeleteBuffers(1, &vbo_coords);
386 if (vbo_normals != 0)
387 fghDeleteBuffers(1, &vbo_normals);
388 if (ibo_elements != 0)
389 fghDeleteBuffers(1, &ibo_elements);
390 if (ibo_elements2 != 0)
391 fghDeleteBuffers(1, &ibo_elements2);
397 /* Version for OpenGL (ES) >= 2.0 */
398 static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices,
399 GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart,
400 GLint attribute_v_coord, GLint attribute_v_normal, GLint attribute_v_texture)
402 GLuint vbo_coords = 0, vbo_normals = 0, vbo_textcs = 0, ibo_elements = 0;
403 GLsizei numVertIdxs = numParts * numVertIdxsPerPart;
406 if (numVertices > 0 && attribute_v_coord != -1) {
407 fghGenBuffers(1, &vbo_coords);
408 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
409 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]),
410 vertices, FGH_STATIC_DRAW);
411 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
414 if (numVertices > 0 && attribute_v_normal != -1) {
415 fghGenBuffers(1, &vbo_normals);
416 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
417 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]),
418 normals, FGH_STATIC_DRAW);
419 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
422 if (numVertices > 0 && attribute_v_texture != -1 && textcs) {
423 fghGenBuffers(1, &vbo_textcs);
424 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs);
425 fghBufferData(FGH_ARRAY_BUFFER, numVertices * 2 * sizeof(textcs[0]),
426 textcs, FGH_STATIC_DRAW);
427 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
430 if (vertIdxs != NULL) {
431 fghGenBuffers(1, &ibo_elements);
432 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
433 fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]),
434 vertIdxs, FGH_STATIC_DRAW);
435 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
439 fghEnableVertexAttribArray(attribute_v_coord);
440 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
441 fghVertexAttribPointer(
442 attribute_v_coord, /* attribute */
443 3, /* number of elements per vertex, here (x,y,z) */
444 GL_FLOAT, /* the type of each element */
445 GL_FALSE, /* take our values as-is */
446 0, /* no extra data between each position */
447 0 /* offset of first element */
449 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
453 fghEnableVertexAttribArray(attribute_v_normal);
454 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals);
455 fghVertexAttribPointer(
456 attribute_v_normal, /* attribute */
457 3, /* number of elements per vertex, here (x,y,z) */
458 GL_FLOAT, /* the type of each element */
459 GL_FALSE, /* take our values as-is */
460 0, /* no extra data between each position */
461 0 /* offset of first element */
463 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
467 fghEnableVertexAttribArray(attribute_v_texture);
468 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs);
469 fghVertexAttribPointer(
470 attribute_v_texture,/* attribute */
471 2, /* number of elements per vertex, here (s,t) */
472 GL_FLOAT, /* the type of each element */
473 GL_FALSE, /* take our values as-is */
474 0, /* no extra data between each position */
475 0 /* offset of first element */
477 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
480 if (vertIdxs == NULL) {
481 glDrawArrays(GL_TRIANGLES, 0, numVertices);
483 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements);
485 for (i=0; i<numParts; i++) {
486 glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertIdxsPerPart));
489 glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, 0);
491 /* Clean existing bindings before clean-up */
492 /* Android showed instability otherwise */
493 fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0);
497 fghDisableVertexAttribArray(attribute_v_coord);
498 if (vbo_normals != 0)
499 fghDisableVertexAttribArray(attribute_v_normal);
501 fghDisableVertexAttribArray(attribute_v_texture);
504 fghDeleteBuffers(1, &vbo_coords);
505 if (vbo_normals != 0)
506 fghDeleteBuffers(1, &vbo_normals);
508 fghDeleteBuffers(1, &vbo_textcs);
509 if (ibo_elements != 0)
510 fghDeleteBuffers(1, &ibo_elements);
516 * Generate vertex indices for visualizing the normals.
517 * vertices are written into verticesForNormalVisualization.
518 * This must be freed by caller, we do the free at the
519 * end of fghDrawNormalVisualization11/fghDrawNormalVisualization20
521 static GLfloat *verticesForNormalVisualization;
522 static GLsizei numNormalVertices = 0;
523 static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices)
526 numNormalVertices = numVertices * 2;
527 verticesForNormalVisualization = malloc(numNormalVertices*3 * sizeof(GLfloat));
529 for (i=0,j=0; i<numNormalVertices*3/2; i+=3, j+=6)
531 verticesForNormalVisualization[j+0] = vertices[i+0];
532 verticesForNormalVisualization[j+1] = vertices[i+1];
533 verticesForNormalVisualization[j+2] = vertices[i+2];
534 verticesForNormalVisualization[j+3] = vertices[i+0] + normals[i+0]/4.f;
535 verticesForNormalVisualization[j+4] = vertices[i+1] + normals[i+1]/4.f;
536 verticesForNormalVisualization[j+5] = vertices[i+2] + normals[i+2]/4.f;
540 /* Version for OpenGL (ES) 1.1 */
541 static void fghDrawNormalVisualization11()
543 GLfloat currentColor[4];
544 /* Setup draw color: (1,1,1)-shape's color */
545 glGetFloatv(GL_CURRENT_COLOR,currentColor);
546 glColor4f(1-currentColor[0],1-currentColor[1],1-currentColor[2],currentColor[3]);
548 glEnableClientState(GL_VERTEX_ARRAY);
550 glVertexPointer(3, GL_FLOAT, 0, verticesForNormalVisualization);
551 glDrawArrays(GL_LINES, 0, numNormalVertices);
553 glDisableClientState(GL_VERTEX_ARRAY);
555 /* Done, free memory, reset color */
556 free(verticesForNormalVisualization);
557 glColor4f(currentColor[0],currentColor[1],currentColor[2],currentColor[3]);
560 /* Version for OpenGL (ES) >= 2.0 */
561 static void fghDrawNormalVisualization20(GLint attribute_v_coord)
563 GLuint vbo_coords = 0;
565 if (attribute_v_coord != -1) {
566 fghGenBuffers(1, &vbo_coords);
567 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
568 fghBufferData(FGH_ARRAY_BUFFER, numNormalVertices * 3 * sizeof(verticesForNormalVisualization[0]),
569 verticesForNormalVisualization, FGH_STATIC_DRAW);
574 fghEnableVertexAttribArray(attribute_v_coord);
575 fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords);
576 fghVertexAttribPointer(
577 attribute_v_coord, /* attribute */
578 3, /* number of elements per vertex, here (x,y,z) */
579 GL_FLOAT, /* the type of each element */
580 GL_FALSE, /* take our values as-is */
581 0, /* no extra data between each position */
582 0 /* offset of first element */
584 fghBindBuffer(FGH_ARRAY_BUFFER, 0);
587 glDrawArrays(GL_LINES, 0, numNormalVertices);
590 fghDisableVertexAttribArray(attribute_v_coord);
593 fghDeleteBuffers(1, &vbo_coords);
595 /* Done, free memory */
596 free(verticesForNormalVisualization);
600 * Generate all combinations of vertices and normals needed to draw object.
601 * Optional shape decomposition to triangles:
602 * We'll use glDrawElements to draw all shapes that are not naturally
603 * composed of triangles, so generate an index vector here, using the
604 * below sampling scheme.
605 * Be careful to keep winding of all triangles counter-clockwise,
606 * assuming that input has correct winding...
608 static GLubyte vert4Decomp[6] = {0,1,2, 0,2,3}; /* quad : 4 input vertices, 6 output (2 triangles) */
609 static GLubyte vert5Decomp[9] = {0,1,2, 0,2,4, 4,2,3}; /* pentagon: 5 input vertices, 9 output (3 triangles) */
611 static void fghGenerateGeometryWithIndexArray(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut, GLushort *vertIdxOut)
613 int i,j,numEdgeIdxPerFace;
614 GLubyte *vertSamps = NULL;
615 switch (numEdgePerFace)
618 /* nothing to do here, we'll draw with glDrawArrays */
621 vertSamps = vert4Decomp;
622 numEdgeIdxPerFace = 6; /* 6 output vertices for each face */
625 vertSamps = vert5Decomp;
626 numEdgeIdxPerFace = 9; /* 9 output vertices for each face */
630 * Build array with vertices using vertex coordinates and vertex indices
631 * Do same for normals.
632 * Need to do this because of different normals at shared vertices.
634 for (i=0; i<numFaces; i++)
637 int faceIdxVertIdx = i*numEdgePerFace; /* index to first element of "row" in vertex indices */
638 for (j=0; j<numEdgePerFace; j++)
640 int outIdx = i*numEdgePerFace*3+j*3;
641 int vertIdx = vertIndices[faceIdxVertIdx+j]*3;
643 vertOut[outIdx ] = vertices[vertIdx ];
644 vertOut[outIdx+1] = vertices[vertIdx+1];
645 vertOut[outIdx+2] = vertices[vertIdx+2];
647 normOut[outIdx ] = normals [normIdx ];
648 normOut[outIdx+1] = normals [normIdx+1];
649 normOut[outIdx+2] = normals [normIdx+2];
652 /* generate vertex indices for each face */
654 for (j=0; j<numEdgeIdxPerFace; j++)
655 vertIdxOut[i*numEdgeIdxPerFace+j] = faceIdxVertIdx + vertSamps[j];
659 static void fghGenerateGeometry(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut)
661 /* This function does the same as fghGenerateGeometryWithIndexArray, just skipping the index array generation... */
662 fghGenerateGeometryWithIndexArray(numFaces, numEdgePerFace, vertices, vertIndices, normals, vertOut, normOut, NULL);
666 /* -- INTERNAL SETUP OF GEOMETRY --------------------------------------- */
667 /* -- stuff that can be cached -- */
668 /* Cache of input to glDrawArrays or glDrawElements
669 * In general, we build arrays with all vertices or normals.
670 * We cant compress this and use glDrawElements as all combinations of
671 * vertices and normals are unique.
673 #define DECLARE_SHAPE_CACHE(name,nameICaps,nameCaps)\
674 static GLboolean name##Cached = GL_FALSE;\
675 static GLfloat name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
676 static GLfloat name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
677 static void fgh##nameICaps##Generate()\
679 fghGenerateGeometry(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
680 name##_v, name##_vi, name##_n,\
681 name##_verts, name##_norms);\
683 #define DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(name,nameICaps,nameCaps)\
684 static GLboolean name##Cached = GL_FALSE;\
685 static GLfloat name##_verts[nameCaps##_VERT_ELEM_PER_OBJ];\
686 static GLfloat name##_norms[nameCaps##_VERT_ELEM_PER_OBJ];\
687 static GLushort name##_vertIdxs[nameCaps##_VERT_PER_OBJ_TRI];\
688 static void fgh##nameICaps##Generate()\
690 fghGenerateGeometryWithIndexArray(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\
691 name##_v, name##_vi, name##_n,\
692 name##_verts, name##_norms, name##_vertIdxs);\
696 #define CUBE_NUM_VERT 8
697 #define CUBE_NUM_FACES 6
698 #define CUBE_NUM_EDGE_PER_FACE 4
699 #define CUBE_VERT_PER_OBJ (CUBE_NUM_FACES*CUBE_NUM_EDGE_PER_FACE)
700 #define CUBE_VERT_ELEM_PER_OBJ (CUBE_VERT_PER_OBJ*3)
701 #define CUBE_VERT_PER_OBJ_TRI (CUBE_VERT_PER_OBJ+CUBE_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
702 /* Vertex Coordinates */
703 static GLfloat cube_v[CUBE_NUM_VERT*3] =
715 static GLfloat cube_n[CUBE_NUM_FACES*3] =
725 /* Vertex indices, as quads, before triangulation */
726 static GLubyte cube_vi[CUBE_VERT_PER_OBJ] =
735 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(cube,Cube,CUBE)
737 /* -- Dodecahedron -- */
738 /* Magic Numbers: It is possible to create a dodecahedron by attaching two
739 * pentagons to each face of of a cube. The coordinates of the points are:
740 * (+-x,0, z); (+-1, 1, 1); (0, z, x )
741 * where x = (-1 + sqrt(5))/2, z = (1 + sqrt(5))/2 or
742 * x = 0.61803398875 and z = 1.61803398875.
744 #define DODECAHEDRON_NUM_VERT 20
745 #define DODECAHEDRON_NUM_FACES 12
746 #define DODECAHEDRON_NUM_EDGE_PER_FACE 5
747 #define DODECAHEDRON_VERT_PER_OBJ (DODECAHEDRON_NUM_FACES*DODECAHEDRON_NUM_EDGE_PER_FACE)
748 #define DODECAHEDRON_VERT_ELEM_PER_OBJ (DODECAHEDRON_VERT_PER_OBJ*3)
749 #define DODECAHEDRON_VERT_PER_OBJ_TRI (DODECAHEDRON_VERT_PER_OBJ+DODECAHEDRON_NUM_FACES*4) /* 4 extra edges per face when drawing pentagons as triangles */
750 /* Vertex Coordinates */
751 static GLfloat dodecahedron_v[DODECAHEDRON_NUM_VERT*3] =
753 0.0f, 1.61803398875f, 0.61803398875f,
755 -0.61803398875f, 0.0f, 1.61803398875f,
756 0.61803398875f, 0.0f, 1.61803398875f,
758 0.0f, 1.61803398875f, -0.61803398875f,
760 0.61803398875f, 0.0f, -1.61803398875f,
761 -0.61803398875f, 0.0f, -1.61803398875f,
762 - 1.0f, 1.0f, - 1.0f,
763 0.0f, -1.61803398875f, 0.61803398875f,
765 - 1.0f, - 1.0f, 1.0f,
766 0.0f, -1.61803398875f, -0.61803398875f,
767 - 1.0f, - 1.0f, - 1.0f,
768 1.0f, - 1.0f, - 1.0f,
769 1.61803398875f, -0.61803398875f, 0.0f,
770 1.61803398875f, 0.61803398875f, 0.0f,
771 -1.61803398875f, 0.61803398875f, 0.0f,
772 -1.61803398875f, -0.61803398875f, 0.0f
775 static GLfloat dodecahedron_n[DODECAHEDRON_NUM_FACES*3] =
777 0.0f, 0.525731112119f, 0.850650808354f,
778 0.0f, 0.525731112119f, -0.850650808354f,
779 0.0f, -0.525731112119f, 0.850650808354f,
780 0.0f, -0.525731112119f, -0.850650808354f,
782 0.850650808354f, 0.0f, 0.525731112119f,
783 -0.850650808354f, 0.0f, 0.525731112119f,
784 0.850650808354f, 0.0f, -0.525731112119f,
785 -0.850650808354f, 0.0f, -0.525731112119f,
787 0.525731112119f, 0.850650808354f, 0.0f,
788 0.525731112119f, -0.850650808354f, 0.0f,
789 -0.525731112119f, 0.850650808354f, 0.0f,
790 -0.525731112119f, -0.850650808354f, 0.0f,
794 static GLubyte dodecahedron_vi[DODECAHEDRON_VERT_PER_OBJ] =
811 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
814 /* -- Icosahedron -- */
815 #define ICOSAHEDRON_NUM_VERT 12
816 #define ICOSAHEDRON_NUM_FACES 20
817 #define ICOSAHEDRON_NUM_EDGE_PER_FACE 3
818 #define ICOSAHEDRON_VERT_PER_OBJ (ICOSAHEDRON_NUM_FACES*ICOSAHEDRON_NUM_EDGE_PER_FACE)
819 #define ICOSAHEDRON_VERT_ELEM_PER_OBJ (ICOSAHEDRON_VERT_PER_OBJ*3)
820 #define ICOSAHEDRON_VERT_PER_OBJ_TRI ICOSAHEDRON_VERT_PER_OBJ
821 /* Vertex Coordinates */
822 static GLfloat icosahedron_v[ICOSAHEDRON_NUM_VERT*3] =
825 0.447213595500f, 0.894427191000f, 0.0f,
826 0.447213595500f, 0.276393202252f, 0.850650808354f,
827 0.447213595500f, -0.723606797748f, 0.525731112119f,
828 0.447213595500f, -0.723606797748f, -0.525731112119f,
829 0.447213595500f, 0.276393202252f, -0.850650808354f,
830 -0.447213595500f, -0.894427191000f, 0.0f,
831 -0.447213595500f, -0.276393202252f, 0.850650808354f,
832 -0.447213595500f, 0.723606797748f, 0.525731112119f,
833 -0.447213595500f, 0.723606797748f, -0.525731112119f,
834 -0.447213595500f, -0.276393202252f, -0.850650808354f,
838 * 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] ) ;
839 * 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] ) ;
840 * 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] ) ;
842 static GLfloat icosahedron_n[ICOSAHEDRON_NUM_FACES*3] =
844 0.760845213037948f, 0.470228201835026f, 0.341640786498800f,
845 0.760845213036861f, -0.179611190632978f, 0.552786404500000f,
846 0.760845213033849f, -0.581234022404097f, 0.0f,
847 0.760845213036861f, -0.179611190632978f, -0.552786404500000f,
848 0.760845213037948f, 0.470228201835026f, -0.341640786498800f,
849 0.179611190628666f, 0.760845213037948f, 0.552786404498399f,
850 0.179611190634277f, -0.290617011204044f, 0.894427191000000f,
851 0.179611190633958f, -0.940456403667806f, 0.0f,
852 0.179611190634278f, -0.290617011204044f, -0.894427191000000f,
853 0.179611190628666f, 0.760845213037948f, -0.552786404498399f,
854 -0.179611190633958f, 0.940456403667806f, 0.0f,
855 -0.179611190634277f, 0.290617011204044f, 0.894427191000000f,
856 -0.179611190628666f, -0.760845213037948f, 0.552786404498399f,
857 -0.179611190628666f, -0.760845213037948f, -0.552786404498399f,
858 -0.179611190634277f, 0.290617011204044f, -0.894427191000000f,
859 -0.760845213036861f, 0.179611190632978f, -0.552786404500000f,
860 -0.760845213033849f, 0.581234022404097f, 0.0f,
861 -0.760845213036861f, 0.179611190632978f, 0.552786404500000f,
862 -0.760845213037948f, -0.470228201835026f, 0.341640786498800f,
863 -0.760845213037948f, -0.470228201835026f, -0.341640786498800f,
867 static GLubyte icosahedron_vi[ICOSAHEDRON_VERT_PER_OBJ] =
890 DECLARE_SHAPE_CACHE(icosahedron,Icosahedron,ICOSAHEDRON)
892 /* -- Octahedron -- */
893 #define OCTAHEDRON_NUM_VERT 6
894 #define OCTAHEDRON_NUM_FACES 8
895 #define OCTAHEDRON_NUM_EDGE_PER_FACE 3
896 #define OCTAHEDRON_VERT_PER_OBJ (OCTAHEDRON_NUM_FACES*OCTAHEDRON_NUM_EDGE_PER_FACE)
897 #define OCTAHEDRON_VERT_ELEM_PER_OBJ (OCTAHEDRON_VERT_PER_OBJ*3)
898 #define OCTAHEDRON_VERT_PER_OBJ_TRI OCTAHEDRON_VERT_PER_OBJ
900 /* Vertex Coordinates */
901 static GLfloat octahedron_v[OCTAHEDRON_NUM_VERT*3] =
912 static GLfloat octahedron_n[OCTAHEDRON_NUM_FACES*3] =
914 0.577350269189f, 0.577350269189f, 0.577350269189f, /* sqrt(1/3) */
915 0.577350269189f, 0.577350269189f,-0.577350269189f,
916 0.577350269189f,-0.577350269189f, 0.577350269189f,
917 0.577350269189f,-0.577350269189f,-0.577350269189f,
918 -0.577350269189f, 0.577350269189f, 0.577350269189f,
919 -0.577350269189f, 0.577350269189f,-0.577350269189f,
920 -0.577350269189f,-0.577350269189f, 0.577350269189f,
921 -0.577350269189f,-0.577350269189f,-0.577350269189f
926 static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OBJ] =
937 DECLARE_SHAPE_CACHE(octahedron,Octahedron,OCTAHEDRON)
939 /* -- RhombicDodecahedron -- */
940 #define RHOMBICDODECAHEDRON_NUM_VERT 14
941 #define RHOMBICDODECAHEDRON_NUM_FACES 12
942 #define RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE 4
943 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ (RHOMBICDODECAHEDRON_NUM_FACES*RHOMBICDODECAHEDRON_NUM_EDGE_PER_FACE)
944 #define RHOMBICDODECAHEDRON_VERT_ELEM_PER_OBJ (RHOMBICDODECAHEDRON_VERT_PER_OBJ*3)
945 #define RHOMBICDODECAHEDRON_VERT_PER_OBJ_TRI (RHOMBICDODECAHEDRON_VERT_PER_OBJ+RHOMBICDODECAHEDRON_NUM_FACES*2) /* 2 extra edges per face when drawing quads as triangles */
947 /* Vertex Coordinates */
948 static GLfloat rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] =
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,
955 0.707106781187f, 0.707106781187f, 0.0f,
956 -0.707106781187f, 0.707106781187f, 0.0f,
957 -0.707106781187f, -0.707106781187f, 0.0f,
958 0.707106781187f, -0.707106781187f, 0.0f,
959 0.707106781187f, 0.0f, -0.5f,
960 0.0f, 0.707106781187f, -0.5f,
961 -0.707106781187f, 0.0f, -0.5f,
962 0.0f, -0.707106781187f, -0.5f,
966 static GLfloat rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] =
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,
976 0.353553390594f, 0.353553390594f, -0.5f,
977 -0.353553390594f, 0.353553390594f, -0.5f,
978 -0.353553390594f, -0.353553390594f, -0.5f,
979 0.353553390594f, -0.353553390594f, -0.5f
983 static GLubyte rhombicdodecahedron_vi[RHOMBICDODECAHEDRON_VERT_PER_OBJ] =
998 DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
1000 /* -- Tetrahedron -- */
1001 /* Magic Numbers: r0 = ( 1, 0, 0 )
1002 * r1 = ( -1/3, 2 sqrt(2) / 3, 0 )
1003 * r2 = ( -1/3, - sqrt(2) / 3, sqrt(6) / 3 )
1004 * r3 = ( -1/3, - sqrt(2) / 3, -sqrt(6) / 3 )
1005 * |r0| = |r1| = |r2| = |r3| = 1
1006 * Distance between any two points is 2 sqrt(6) / 3
1008 * Normals: The unit normals are simply the negative of the coordinates of the point not on the surface.
1010 #define TETRAHEDRON_NUM_VERT 4
1011 #define TETRAHEDRON_NUM_FACES 4
1012 #define TETRAHEDRON_NUM_EDGE_PER_FACE 3
1013 #define TETRAHEDRON_VERT_PER_OBJ (TETRAHEDRON_NUM_FACES*TETRAHEDRON_NUM_EDGE_PER_FACE)
1014 #define TETRAHEDRON_VERT_ELEM_PER_OBJ (TETRAHEDRON_VERT_PER_OBJ*3)
1015 #define TETRAHEDRON_VERT_PER_OBJ_TRI TETRAHEDRON_VERT_PER_OBJ
1017 /* Vertex Coordinates */
1018 static GLfloat tetrahedron_v[TETRAHEDRON_NUM_VERT*3] =
1021 -0.333333333333f, 0.942809041582f, 0.0f,
1022 -0.333333333333f, -0.471404520791f, 0.816496580928f,
1023 -0.333333333333f, -0.471404520791f, -0.816496580928f
1025 /* Normal Vectors */
1026 static GLfloat tetrahedron_n[TETRAHEDRON_NUM_FACES*3] =
1029 0.333333333333f, -0.942809041582f, 0.0f,
1030 0.333333333333f, 0.471404520791f, -0.816496580928f,
1031 0.333333333333f, 0.471404520791f, 0.816496580928f
1034 /* Vertex indices */
1035 static GLubyte tetrahedron_vi[TETRAHEDRON_VERT_PER_OBJ] =
1042 DECLARE_SHAPE_CACHE(tetrahedron,Tetrahedron,TETRAHEDRON)
1044 /* -- Sierpinski Sponge -- */
1045 static unsigned int ipow (int x, unsigned int y)
1047 /* return y==0? 1: y==1? x: (y%2? x: 1) * ipow(x*x, y/2); */
1056 return (y%2? x: 1) * ipow(x*x, y/2);
1061 static void fghSierpinskiSpongeGenerate ( int numLevels, double offset[3], GLfloat scale, GLfloat* vertices, GLfloat* normals )
1064 if ( numLevels == 0 )
1066 for (i=0; i<TETRAHEDRON_NUM_FACES; i++)
1069 int faceIdxVertIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE;
1070 for (j=0; j<TETRAHEDRON_NUM_EDGE_PER_FACE; j++)
1072 int outIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE*3+j*3;
1073 int vertIdx = tetrahedron_vi[faceIdxVertIdx+j]*3;
1075 vertices[outIdx ] = (GLfloat)offset[0] + scale * tetrahedron_v[vertIdx ];
1076 vertices[outIdx+1] = (GLfloat)offset[1] + scale * tetrahedron_v[vertIdx+1];
1077 vertices[outIdx+2] = (GLfloat)offset[2] + scale * tetrahedron_v[vertIdx+2];
1079 normals [outIdx ] = tetrahedron_n[normIdx ];
1080 normals [outIdx+1] = tetrahedron_n[normIdx+1];
1081 normals [outIdx+2] = tetrahedron_n[normIdx+2];
1085 else if ( numLevels > 0 )
1087 double local_offset[3] ; /* Use a local variable to avoid buildup of roundoff errors */
1088 unsigned int stride = ipow(4,--numLevels)*TETRAHEDRON_VERT_ELEM_PER_OBJ;
1090 for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ )
1093 local_offset[0] = offset[0] + scale * tetrahedron_v[idx ];
1094 local_offset[1] = offset[1] + scale * tetrahedron_v[idx+1];
1095 local_offset[2] = offset[2] + scale * tetrahedron_v[idx+2];
1096 fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride );
1101 /* -- Now the various non-polyhedra (shapes involving circles) -- */
1103 * Compute lookup table of cos and sin values forming a circle
1104 * (or half circle if halfCircle==TRUE)
1107 * It is the responsibility of the caller to free these tables
1108 * The size of the table is (n+1) to form a connected loop
1109 * The last entry is exactly the same as the first
1110 * The sign of n can be flipped to get the reverse loop
1112 static void fghCircleTable(GLfloat **sint, GLfloat **cost, const int n, const GLboolean halfCircle)
1116 /* Table size, the sign of n flips the circle direction */
1117 const int size = abs(n);
1119 /* Determine the angle between samples */
1120 const GLfloat angle = (halfCircle?1:2)*(GLfloat)M_PI/(GLfloat)( ( n == 0 ) ? 1 : n );
1122 /* Allocate memory for n samples, plus duplicate of first entry at the end */
1123 *sint = malloc(sizeof(GLfloat) * (size+1));
1124 *cost = malloc(sizeof(GLfloat) * (size+1));
1126 /* Bail out if memory allocation fails, fgError never returns */
1127 if (!(*sint) || !(*cost))
1131 fgError("Failed to allocate memory in fghCircleTable");
1134 /* Compute cos and sin around the circle */
1138 for (i=1; i<size; i++)
1140 (*sint)[i] = (GLfloat)sin(angle*i);
1141 (*cost)[i] = (GLfloat)cos(angle*i);
1147 (*sint)[size] = 0.0f; /* sin PI */
1148 (*cost)[size] = -1.0f; /* cos PI */
1152 /* Last sample is duplicate of the first (sin or cos of 2 PI) */
1153 (*sint)[size] = (*sint)[0];
1154 (*cost)[size] = (*cost)[0];
1158 static void fghGenerateSphere(GLfloat radius, GLint slices, GLint stacks, GLfloat **vertices, GLfloat **normals, int* nVert)
1161 int idx = 0; /* idx into vertex/normal buffer */
1164 /* Pre-computed circle */
1165 GLfloat *sint1,*cost1;
1166 GLfloat *sint2,*cost2;
1168 /* number of unique vertices */
1169 if (slices==0 || stacks<2)
1171 /* nothing to generate */
1175 *nVert = slices*(stacks-1)+2;
1176 if ((*nVert) > 65535)
1178 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1180 fgWarning("fghGenerateSphere: too many slices or stacks requested, indices will wrap");
1182 /* precompute values on unit circle */
1183 fghCircleTable(&sint1,&cost1,-slices,GL_FALSE);
1184 fghCircleTable(&sint2,&cost2, stacks,GL_TRUE);
1186 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1187 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1188 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1189 if (!(*vertices) || !(*normals))
1193 fgError("Failed to allocate memory in fghGenerateSphere");
1197 (*vertices)[0] = 0.f;
1198 (*vertices)[1] = 0.f;
1199 (*vertices)[2] = radius;
1200 (*normals )[0] = 0.f;
1201 (*normals )[1] = 0.f;
1202 (*normals )[2] = 1.f;
1206 for( i=1; i<stacks; i++ )
1208 for(j=0; j<slices; j++, idx+=3)
1210 x = cost1[j]*sint2[i];
1211 y = sint1[j]*sint2[i];
1214 (*vertices)[idx ] = x*radius;
1215 (*vertices)[idx+1] = y*radius;
1216 (*vertices)[idx+2] = z*radius;
1217 (*normals )[idx ] = x;
1218 (*normals )[idx+1] = y;
1219 (*normals )[idx+2] = z;
1224 (*vertices)[idx ] = 0.f;
1225 (*vertices)[idx+1] = 0.f;
1226 (*vertices)[idx+2] = -radius;
1227 (*normals )[idx ] = 0.f;
1228 (*normals )[idx+1] = 0.f;
1229 (*normals )[idx+2] = -1.f;
1231 /* Done creating vertices, release sin and cos tables */
1238 void fghGenerateCone(
1239 GLfloat base, GLfloat height, GLint slices, GLint stacks, /* input */
1240 GLfloat **vertices, GLfloat **normals, int* nVert /* output */
1244 int idx = 0; /* idx into vertex/normal buffer */
1246 /* Pre-computed circle */
1247 GLfloat *sint,*cost;
1249 /* Step in z and radius as stacks are drawn. */
1251 GLfloat r = (GLfloat)base;
1253 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1254 const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 );
1256 /* Scaling factors for vertex normals */
1257 const GLfloat cosn = (GLfloat) (height / sqrt( height * height + base * base ));
1258 const GLfloat sinn = (GLfloat) (base / sqrt( height * height + base * base ));
1262 /* number of unique vertices */
1263 if (slices==0 || stacks<1)
1265 /* nothing to generate */
1269 *nVert = slices*(stacks+2)+1; /* need an extra stack for closing off bottom with correct normals */
1271 if ((*nVert) > 65535)
1273 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1275 fgWarning("fghGenerateCone: too many slices or stacks requested, indices will wrap");
1277 /* Pre-computed circle */
1278 fghCircleTable(&sint,&cost,-slices,GL_FALSE);
1280 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1281 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1282 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1283 if (!(*vertices) || !(*normals))
1287 fgError("Failed to allocate memory in fghGenerateCone");
1291 (*vertices)[0] = 0.f;
1292 (*vertices)[1] = 0.f;
1294 (*normals )[0] = 0.f;
1295 (*normals )[1] = 0.f;
1296 (*normals )[2] = -1.f;
1298 /* other on bottom (get normals right) */
1299 for (j=0; j<slices; j++, idx+=3)
1301 (*vertices)[idx ] = cost[j]*r;
1302 (*vertices)[idx+1] = sint[j]*r;
1303 (*vertices)[idx+2] = z;
1304 (*normals )[idx ] = 0.f;
1305 (*normals )[idx+1] = 0.f;
1306 (*normals )[idx+2] = -1.f;
1310 for (i=0; i<stacks+1; i++ )
1312 for (j=0; j<slices; j++, idx+=3)
1314 (*vertices)[idx ] = cost[j]*r;
1315 (*vertices)[idx+1] = sint[j]*r;
1316 (*vertices)[idx+2] = z;
1317 (*normals )[idx ] = cost[j]*cosn;
1318 (*normals )[idx+1] = sint[j]*cosn;
1319 (*normals )[idx+2] = sinn;
1326 /* Release sin and cos tables */
1331 void fghGenerateCylinder(
1332 GLfloat radius, GLfloat height, GLint slices, GLint stacks, /* input */
1333 GLfloat **vertices, GLfloat **normals, int* nVert /* output */
1337 int idx = 0; /* idx into vertex/normal buffer */
1339 /* Step in z as stacks are drawn. */
1340 GLfloat radf = (GLfloat)radius;
1342 const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 );
1344 /* Pre-computed circle */
1345 GLfloat *sint,*cost;
1347 /* number of unique vertices */
1348 if (slices==0 || stacks<1)
1350 /* nothing to generate */
1354 *nVert = slices*(stacks+3)+2; /* need two extra stacks for closing off top and bottom with correct normals */
1356 if ((*nVert) > 65535)
1358 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1360 fgWarning("fghGenerateCylinder: too many slices or stacks requested, indices will wrap");
1362 /* Pre-computed circle */
1363 fghCircleTable(&sint,&cost,-slices,GL_FALSE);
1365 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1366 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1367 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1368 if (!(*vertices) || !(*normals))
1372 fgError("Failed to allocate memory in fghGenerateCylinder");
1377 (*vertices)[0] = 0.f;
1378 (*vertices)[1] = 0.f;
1379 (*vertices)[2] = 0.f;
1380 (*normals )[0] = 0.f;
1381 (*normals )[1] = 0.f;
1382 (*normals )[2] = -1.f;
1384 /* other on top (get normals right) */
1385 for (j=0; j<slices; j++, idx+=3)
1387 (*vertices)[idx ] = cost[j]*radf;
1388 (*vertices)[idx+1] = sint[j]*radf;
1389 (*vertices)[idx+2] = z;
1390 (*normals )[idx ] = 0.f;
1391 (*normals )[idx+1] = 0.f;
1392 (*normals )[idx+2] = -1.f;
1396 for (i=0; i<stacks+1; i++ )
1398 for (j=0; j<slices; j++, idx+=3)
1400 (*vertices)[idx ] = cost[j]*radf;
1401 (*vertices)[idx+1] = sint[j]*radf;
1402 (*vertices)[idx+2] = z;
1403 (*normals )[idx ] = cost[j];
1404 (*normals )[idx+1] = sint[j];
1405 (*normals )[idx+2] = 0.f;
1411 /* other on bottom (get normals right) */
1413 for (j=0; j<slices; j++, idx+=3)
1415 (*vertices)[idx ] = cost[j]*radf;
1416 (*vertices)[idx+1] = sint[j]*radf;
1417 (*vertices)[idx+2] = z;
1418 (*normals )[idx ] = 0.f;
1419 (*normals )[idx+1] = 0.f;
1420 (*normals )[idx+2] = 1.f;
1424 (*vertices)[idx ] = 0.f;
1425 (*vertices)[idx+1] = 0.f;
1426 (*vertices)[idx+2] = height;
1427 (*normals )[idx ] = 0.f;
1428 (*normals )[idx+1] = 0.f;
1429 (*normals )[idx+2] = 1.f;
1431 /* Release sin and cos tables */
1436 void fghGenerateTorus(
1437 double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings, /* input */
1438 GLfloat **vertices, GLfloat **normals, int* nVert /* output */
1441 GLfloat iradius = (float)dInnerRadius;
1442 GLfloat oradius = (float)dOuterRadius;
1445 /* Pre-computed circle */
1446 GLfloat *spsi, *cpsi;
1447 GLfloat *sphi, *cphi;
1449 /* number of unique vertices */
1450 if (nSides<2 || nRings<2)
1452 /* nothing to generate */
1456 *nVert = nSides * nRings;
1458 if ((*nVert) > 65535)
1460 * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
1462 fgWarning("fghGenerateTorus: too many slices or stacks requested, indices will wrap");
1464 /* precompute values on unit circle */
1465 fghCircleTable(&spsi,&cpsi, nRings,GL_FALSE);
1466 fghCircleTable(&sphi,&cphi,-nSides,GL_FALSE);
1468 /* Allocate vertex and normal buffers, bail out if memory allocation fails */
1469 *vertices = malloc((*nVert)*3*sizeof(GLfloat));
1470 *normals = malloc((*nVert)*3*sizeof(GLfloat));
1471 if (!(*vertices) || !(*normals))
1475 fgError("Failed to allocate memory in fghGenerateTorus");
1478 for( j=0; j<nRings; j++ )
1480 for( i=0; i<nSides; i++ )
1482 int offset = 3 * ( j * nSides + i ) ;
1484 (*vertices)[offset ] = cpsi[j] * ( oradius + cphi[i] * iradius ) ;
1485 (*vertices)[offset+1] = spsi[j] * ( oradius + cphi[i] * iradius ) ;
1486 (*vertices)[offset+2] = sphi[i] * iradius ;
1487 (*normals )[offset ] = cpsi[j] * cphi[i] ;
1488 (*normals )[offset+1] = spsi[j] * cphi[i] ;
1489 (*normals )[offset+2] = sphi[i] ;
1493 /* Release sin and cos tables */
1500 /* -- INTERNAL DRAWING functions --------------------------------------- */
1501 #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\
1502 static void fgh##nameICaps( GLboolean useWireMode )\
1506 fgh##nameICaps##Generate();\
1507 name##Cached = GL_TRUE;\
1512 fghDrawGeometryWire (name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ, \
1513 NULL,nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE,GL_LINE_LOOP,\
1518 fghDrawGeometrySolid(name##_verts,name##_norms,NULL,nameCaps##_VERT_PER_OBJ,\
1519 vertIdxs, 1, nameCaps##_VERT_PER_OBJ_TRI); \
1522 #define DECLARE_INTERNAL_DRAW(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,NULL)
1523 #define DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(name,nameICaps,nameCaps) _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,name##_vertIdxs)
1525 static void fghCube( GLfloat dSize, GLboolean useWireMode )
1532 cubeCached = GL_TRUE;
1537 /* Need to build new vertex list containing vertices for cube of different size */
1540 vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat));
1542 /* Bail out if memory allocation fails, fgError never returns */
1546 fgError("Failed to allocate memory in fghCube");
1549 for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++)
1550 vertices[i] = dSize*cube_verts[i];
1553 vertices = cube_verts;
1556 fghDrawGeometryWire(vertices, cube_norms, CUBE_VERT_PER_OBJ,
1557 NULL,CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1560 fghDrawGeometrySolid(vertices, cube_norms, NULL, CUBE_VERT_PER_OBJ,
1561 cube_vertIdxs, 1, CUBE_VERT_PER_OBJ_TRI);
1564 /* cleanup allocated memory */
1568 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON)
1569 DECLARE_INTERNAL_DRAW(icosahedron,Icosahedron,ICOSAHEDRON)
1570 DECLARE_INTERNAL_DRAW(octahedron,Octahedron,OCTAHEDRON)
1571 DECLARE_INTERNAL_DRAW_DECOMPOSED_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON)
1572 DECLARE_INTERNAL_DRAW(tetrahedron,Tetrahedron,TETRAHEDRON)
1574 static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode )
1578 GLsizei numTetr = numLevels<0? 0 : ipow(4,numLevels); /* No sponge for numLevels below 0 */
1579 GLsizei numVert = numTetr*TETRAHEDRON_VERT_PER_OBJ;
1580 GLsizei numFace = numTetr*TETRAHEDRON_NUM_FACES;
1584 /* Allocate memory */
1585 vertices = malloc(numVert*3 * sizeof(GLfloat));
1586 normals = malloc(numVert*3 * sizeof(GLfloat));
1587 /* Bail out if memory allocation fails, fgError never returns */
1588 if (!vertices || !normals)
1592 fgError("Failed to allocate memory in fghSierpinskiSponge");
1595 /* Generate elements */
1596 fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals );
1598 /* Draw and cleanup */
1600 fghDrawGeometryWire (vertices,normals,numVert,
1601 NULL,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE,GL_LINE_LOOP,
1604 fghDrawGeometrySolid(vertices,normals,NULL,numVert,NULL,1,0);
1612 static void fghSphere( GLfloat radius, GLint slices, GLint stacks, GLboolean useWireMode )
1615 GLfloat *vertices, *normals;
1617 /* Generate vertices and normals */
1618 fghGenerateSphere(radius,slices,stacks,&vertices,&normals,&nVert);
1621 /* nothing to draw */
1626 GLushort *sliceIdx, *stackIdx;
1627 /* First, generate vertex index arrays for drawing with glDrawElements
1628 * We have a bunch of line_loops to draw for each stack, and a
1629 * bunch for each slice.
1632 sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1633 stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort));
1634 if (!(stackIdx) || !(sliceIdx))
1638 fgError("Failed to allocate memory in fghSphere");
1641 /* generate for each stack */
1642 for (i=0,idx=0; i<stacks-1; i++)
1644 GLushort offset = 1+i*slices; /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1645 for (j=0; j<slices; j++, idx++)
1647 stackIdx[idx] = offset+j;
1651 /* generate for each slice */
1652 for (i=0,idx=0; i<slices; i++)
1654 GLushort offset = 1+i; /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1655 sliceIdx[idx++] = 0; /* vertex on top */
1656 for (j=0; j<stacks-1; j++, idx++)
1658 sliceIdx[idx] = offset+j*slices;
1660 sliceIdx[idx++] = nVert-1; /* zero based index, last element in array... */
1664 fghDrawGeometryWire(vertices,normals,nVert,
1665 sliceIdx,slices,stacks+1,GL_LINE_STRIP,
1666 stackIdx,stacks-1,slices);
1668 /* cleanup allocated memory */
1674 /* First, generate vertex index arrays for drawing with glDrawElements
1675 * All stacks, including top and bottom are covered with a triangle
1679 /* Create index vector */
1682 /* Allocate buffers for indices, bail out if memory allocation fails */
1683 stripIdx = malloc((slices+1)*2*(stacks)*sizeof(GLushort));
1687 fgError("Failed to allocate memory in fghSphere");
1691 for (j=0, idx=0; j<slices; j++, idx+=2)
1693 stripIdx[idx ] = j+1; /* 0 is top vertex, 1 is first for first stack */
1694 stripIdx[idx+1] = 0;
1696 stripIdx[idx ] = 1; /* repeat first slice's idx for closing off shape */
1697 stripIdx[idx+1] = 0;
1700 /* middle stacks: */
1701 /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1702 for (i=0; i<stacks-2; i++, idx+=2)
1704 offset = 1+i*slices; /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */
1705 for (j=0; j<slices; j++, idx+=2)
1707 stripIdx[idx ] = offset+j+slices;
1708 stripIdx[idx+1] = offset+j;
1710 stripIdx[idx ] = offset+slices; /* repeat first slice's idx for closing off shape */
1711 stripIdx[idx+1] = offset;
1715 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 */
1716 for (j=0; j<slices; j++, idx+=2)
1718 stripIdx[idx ] = nVert-1; /* zero based index, last element in array (bottom vertex)... */
1719 stripIdx[idx+1] = offset+j;
1721 stripIdx[idx ] = nVert-1; /* repeat first slice's idx for closing off shape */
1722 stripIdx[idx+1] = offset;
1726 fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,stacks,(slices+1)*2);
1728 /* cleanup allocated memory */
1732 /* cleanup allocated memory */
1737 static void fghCone( GLfloat base, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1740 GLfloat *vertices, *normals;
1742 /* Generate vertices and normals */
1743 /* Note, (stacks+1)*slices vertices for side of object, slices+1 for top and bottom closures */
1744 fghGenerateCone(base,height,slices,stacks,&vertices,&normals,&nVert);
1747 /* nothing to draw */
1752 GLushort *sliceIdx, *stackIdx;
1753 /* First, generate vertex index arrays for drawing with glDrawElements
1754 * We have a bunch of line_loops to draw for each stack, and a
1755 * bunch for each slice.
1758 stackIdx = malloc(slices*stacks*sizeof(GLushort));
1759 sliceIdx = malloc(slices*2 *sizeof(GLushort));
1760 if (!(stackIdx) || !(sliceIdx))
1764 fgError("Failed to allocate memory in fghCone");
1767 /* generate for each stack */
1768 for (i=0,idx=0; i<stacks; i++)
1770 GLushort offset = 1+(i+1)*slices; /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1771 for (j=0; j<slices; j++, idx++)
1773 stackIdx[idx] = offset+j;
1777 /* generate for each slice */
1778 for (i=0,idx=0; i<slices; i++)
1780 GLushort offset = 1+i; /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1781 sliceIdx[idx++] = offset+slices;
1782 sliceIdx[idx++] = offset+(stacks+1)*slices;
1786 fghDrawGeometryWire(vertices,normals,nVert,
1787 sliceIdx,1,slices*2,GL_LINES,
1788 stackIdx,stacks,slices);
1790 /* cleanup allocated memory */
1796 /* First, generate vertex index arrays for drawing with glDrawElements
1797 * All stacks, including top and bottom are covered with a triangle
1801 /* Create index vector */
1804 /* Allocate buffers for indices, bail out if memory allocation fails */
1805 stripIdx = malloc((slices+1)*2*(stacks+1)*sizeof(GLushort)); /*stacks +1 because of closing off bottom */
1809 fgError("Failed to allocate memory in fghCone");
1813 for (j=0, idx=0; j<slices; j++, idx+=2)
1816 stripIdx[idx+1] = j+1; /* 0 is top vertex, 1 is first for first stack */
1818 stripIdx[idx ] = 0; /* repeat first slice's idx for closing off shape */
1819 stripIdx[idx+1] = 1;
1822 /* middle stacks: */
1823 /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1824 for (i=0; i<stacks; i++, idx+=2)
1826 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 */
1827 for (j=0; j<slices; j++, idx+=2)
1829 stripIdx[idx ] = offset+j;
1830 stripIdx[idx+1] = offset+j+slices;
1832 stripIdx[idx ] = offset; /* repeat first slice's idx for closing off shape */
1833 stripIdx[idx+1] = offset+slices;
1837 fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,stacks+1,(slices+1)*2);
1839 /* cleanup allocated memory */
1843 /* cleanup allocated memory */
1848 static void fghCylinder( GLfloat radius, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode )
1851 GLfloat *vertices, *normals;
1853 /* Generate vertices and normals */
1854 /* Note, (stacks+1)*slices vertices for side of object, 2*slices+2 for top and bottom closures */
1855 fghGenerateCylinder(radius,height,slices,stacks,&vertices,&normals,&nVert);
1858 /* nothing to draw */
1863 GLushort *sliceIdx, *stackIdx;
1864 /* First, generate vertex index arrays for drawing with glDrawElements
1865 * We have a bunch of line_loops to draw for each stack, and a
1866 * bunch for each slice.
1869 stackIdx = malloc(slices*(stacks+1)*sizeof(GLushort));
1870 sliceIdx = malloc(slices*2 *sizeof(GLushort));
1871 if (!(stackIdx) || !(sliceIdx))
1875 fgError("Failed to allocate memory in fghCylinder");
1878 /* generate for each stack */
1879 for (i=0,idx=0; i<stacks+1; i++)
1881 GLushort offset = 1+(i+1)*slices; /* start at 1 (0 is top vertex), and we advance one stack down as we go along */
1882 for (j=0; j<slices; j++, idx++)
1884 stackIdx[idx] = offset+j;
1888 /* generate for each slice */
1889 for (i=0,idx=0; i<slices; i++)
1891 GLushort offset = 1+i; /* start at 1 (0 is top vertex), and we advance one slice as we go along */
1892 sliceIdx[idx++] = offset+slices;
1893 sliceIdx[idx++] = offset+(stacks+1)*slices;
1897 fghDrawGeometryWire(vertices,normals,nVert,
1898 sliceIdx,1,slices*2,GL_LINES,
1899 stackIdx,stacks+1,slices);
1901 /* cleanup allocated memory */
1907 /* First, generate vertex index arrays for drawing with glDrawElements
1908 * All stacks, including top and bottom are covered with a triangle
1912 /* Create index vector */
1915 /* Allocate buffers for indices, bail out if memory allocation fails */
1916 stripIdx = malloc((slices+1)*2*(stacks+2)*sizeof(GLushort)); /*stacks +2 because of closing off bottom and top */
1920 fgError("Failed to allocate memory in fghCylinder");
1924 for (j=0, idx=0; j<slices; j++, idx+=2)
1927 stripIdx[idx+1] = j+1; /* 0 is top vertex, 1 is first for first stack */
1929 stripIdx[idx ] = 0; /* repeat first slice's idx for closing off shape */
1930 stripIdx[idx+1] = 1;
1933 /* middle stacks: */
1934 /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */
1935 for (i=0; i<stacks; i++, idx+=2)
1937 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 */
1938 for (j=0; j<slices; j++, idx+=2)
1940 stripIdx[idx ] = offset+j;
1941 stripIdx[idx+1] = offset+j+slices;
1943 stripIdx[idx ] = offset; /* repeat first slice's idx for closing off shape */
1944 stripIdx[idx+1] = offset+slices;
1948 offset = 1+(stacks+2)*slices;
1949 for (j=0; j<slices; j++, idx+=2)
1951 stripIdx[idx ] = offset+j;
1952 stripIdx[idx+1] = nVert-1; /* zero based index, last element in array (bottom vertex)... */
1954 stripIdx[idx ] = offset;
1955 stripIdx[idx+1] = nVert-1; /* repeat first slice's idx for closing off shape */
1958 fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,stacks+2,(slices+1)*2);
1960 /* cleanup allocated memory */
1964 /* cleanup allocated memory */
1969 static void fghTorus( GLfloat dInnerRadius, GLfloat dOuterRadius, GLint nSides, GLint nRings, GLboolean useWireMode )
1972 GLfloat *vertices, *normals;
1974 /* Generate vertices and normals */
1975 fghGenerateTorus(dInnerRadius,dOuterRadius,nSides,nRings, &vertices,&normals,&nVert);
1978 /* nothing to draw */
1983 GLushort *sideIdx, *ringIdx;
1984 /* First, generate vertex index arrays for drawing with glDrawElements
1985 * We have a bunch of line_loops to draw each side, and a
1986 * bunch for each ring.
1989 ringIdx = malloc(nRings*nSides*sizeof(GLushort));
1990 sideIdx = malloc(nSides*nRings*sizeof(GLushort));
1991 if (!(ringIdx) || !(sideIdx))
1995 fgError("Failed to allocate memory in fghTorus");
1998 /* generate for each ring */
1999 for( j=0,idx=0; j<nRings; j++ )
2000 for( i=0; i<nSides; i++, idx++ )
2001 ringIdx[idx] = j * nSides + i;
2003 /* generate for each side */
2004 for( i=0,idx=0; i<nSides; i++ )
2005 for( j=0; j<nRings; j++, idx++ )
2006 sideIdx[idx] = j * nSides + i;
2009 fghDrawGeometryWire(vertices,normals,nVert,
2010 ringIdx,nRings,nSides,GL_LINE_LOOP,
2011 sideIdx,nSides,nRings);
2013 /* cleanup allocated memory */
2019 /* First, generate vertex index arrays for drawing with glDrawElements
2020 * All stacks, including top and bottom are covered with a triangle
2025 /* Allocate buffers for indices, bail out if memory allocation fails */
2026 stripIdx = malloc((nRings+1)*2*nSides*sizeof(GLushort));
2030 fgError("Failed to allocate memory in fghTorus");
2033 for( i=0, idx=0; i<nSides; i++ )
2039 for( j=0; j<nRings; j++, idx+=2 )
2041 int offset = j * nSides + i;
2042 stripIdx[idx ] = offset;
2043 stripIdx[idx+1] = offset + ioff;
2045 /* repeat first to close off shape */
2047 stripIdx[idx+1] = i + ioff;
2052 fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,nSides,(nRings+1)*2);
2054 /* cleanup allocated memory */
2058 /* cleanup allocated memory */
2064 /* -- INTERFACE FUNCTIONS ---------------------------------------------- */
2068 * Draws a solid sphere
2070 void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
2072 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" );
2073 fghSphere((GLfloat)radius, slices, stacks, GL_FALSE );
2077 * Draws a wire sphere
2079 void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
2081 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" );
2082 fghSphere((GLfloat)radius, slices, stacks, GL_TRUE );
2087 * Draws a solid cone
2089 void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks )
2091 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" );
2092 fghCone((GLfloat)base, (GLfloat)height, slices, stacks, GL_FALSE );
2098 void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks)
2100 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" );
2101 fghCone((GLfloat)base, (GLfloat)height, slices, stacks, GL_TRUE );
2106 * Draws a solid cylinder
2108 void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks)
2110 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" );
2111 fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, GL_FALSE );
2115 * Draws a wire cylinder
2117 void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks)
2119 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" );
2120 fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, GL_TRUE );
2124 * Draws a wire torus
2126 void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
2128 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" );
2129 fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, GL_TRUE);
2133 * Draws a solid torus
2135 void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings )
2137 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" );
2138 fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, GL_FALSE);
2143 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
2144 /* Macro to generate interface functions */
2145 #define DECLARE_SHAPE_INTERFACE(nameICaps)\
2146 void FGAPIENTRY glutWire##nameICaps( void )\
2148 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\
2149 fgh##nameICaps( GL_TRUE );\
2151 void FGAPIENTRY glutSolid##nameICaps( void )\
2153 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\
2154 fgh##nameICaps( GL_FALSE );\
2157 void FGAPIENTRY glutWireCube( double dSize )
2159 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" );
2160 fghCube( (GLfloat)dSize, GL_TRUE );
2162 void FGAPIENTRY glutSolidCube( double dSize )
2164 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" );
2165 fghCube( (GLfloat)dSize, GL_FALSE );
2168 DECLARE_SHAPE_INTERFACE(Dodecahedron)
2169 DECLARE_SHAPE_INTERFACE(Icosahedron)
2170 DECLARE_SHAPE_INTERFACE(Octahedron)
2171 DECLARE_SHAPE_INTERFACE(RhombicDodecahedron)
2173 void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale )
2175 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" );
2176 fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, GL_TRUE );
2178 void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale )
2180 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" );
2181 fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, GL_FALSE );
2184 DECLARE_SHAPE_INTERFACE(Tetrahedron)
2187 /*** END OF FILE ***/