1 | /* |
2 | * fg_geometry.c |
3 | * |
4 | * Freeglut geometry rendering methods. |
5 | * |
6 | * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. |
7 | * Written by Pawel W. Olszta, <olszta@sourceforge.net> |
8 | * Creation date: Fri Dec 3 1999 |
9 | * |
10 | * Permission is hereby granted, free of charge, to any person obtaining a |
11 | * copy of this software and associated documentation files (the "Software"), |
12 | * to deal in the Software without restriction, including without limitation |
13 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
14 | * and/or sell copies of the Software, and to permit persons to whom the |
15 | * Software is furnished to do so, subject to the following conditions: |
16 | * |
17 | * The above copyright notice and this permission notice shall be included |
18 | * in all copies or substantial portions of the Software. |
19 | * |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
23 | * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
24 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
25 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
26 | */ |
27 | |
28 | #include <GL/freeglut.h> |
29 | #include "fg_internal.h" |
30 | #include "fg_gl2.h" |
31 | #include <math.h> |
32 | |
33 | /* |
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... |
43 | */ |
44 | |
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 |
50 | ); |
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 |
57 | ); |
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); |
65 | |
66 | /* Drawing geometry: |
67 | * Explanation of the functions has to be separate for the polyhedra and |
68 | * the non-polyhedra (objects with a circular cross-section). |
69 | * Polyhedra: |
70 | * - We have only implemented the five platonic solids and the rhomboid |
71 | * dodecahedron. If you need more types of polyhedra, please see |
72 | * CPolyhedron in MRPT |
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. |
82 | * |
83 | * Non-polyhedra: |
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 |
90 | * on them here. |
91 | * - Solids are drawn using glDrawArrays and GL_TRIANGLE_STRIP. Each |
92 | * strip covers one revolution around one of the two subdivision axes |
93 | * of the shape. |
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. |
102 | */ |
103 | |
104 | |
105 | /** |
106 | * Draw geometric shape in wire mode (only edges) |
107 | * |
108 | * Arguments: |
109 | * GLfloat *vertices, GLfloat *normals, GLsizei numVertices |
110 | * The vertex coordinate and normal buffers, and the number of entries in |
111 | * those |
112 | * GLushort *vertIdxs |
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 |
116 | * each face |
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 |
122 | * array vertIdxs |
123 | * GLenum vertexMode |
124 | * vertex drawing mode (e.g. always GL_LINE_LOOP for polyhedra, varies |
125 | * for others) |
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. |
129 | * |
130 | * Feel free to contribute better naming ;) |
131 | */ |
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 |
135 | ) |
136 | { |
137 | GLint attribute_v_coord = fgStructure.CurrentWindow->Window.attribute_v_coord; |
138 | GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal; |
139 | |
140 | if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1)) |
141 | /* User requested a 2.0 draw */ |
142 | fghDrawGeometryWire20(vertices, normals, numVertices, |
143 | vertIdxs, numParts, numVertPerPart, vertexMode, |
144 | vertIdxs2, numParts2, numVertPerPart2, |
145 | attribute_v_coord, attribute_v_normal); |
146 | else |
147 | fghDrawGeometryWire11(vertices, normals, |
148 | vertIdxs, numParts, numVertPerPart, vertexMode, |
149 | vertIdxs2, numParts2, numVertPerPart2); |
150 | } |
151 | |
152 | /* Draw the geometric shape with filled triangles |
153 | * |
154 | * Arguments: |
155 | * GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices |
156 | * The vertex coordinate, normal and texture coordinate buffers, and the |
157 | * number of entries in those |
158 | * GLushort *vertIdxs |
159 | * a vertex indices buffer, optional (not passed for the polyhedra with |
160 | * triangular faces) |
161 | * GLsizei numParts, GLsizei numVertPerPart |
162 | * polyhedra: not used for polyhedra with triangular faces |
163 | (numEdgePerFace==3), as each vertex+normal pair is drawn only once, |
164 | so no vertex indices are used. |
165 | Else, the shape was triangulated (DECOMPOSE_TO_TRIANGLE), leading to |
166 | reuse of some vertex+normal pairs, and thus the need to draw with |
167 | glDrawElements. numParts is always 1 in this case (we can draw the |
168 | whole object with one call to glDrawElements as the vertex index |
169 | array contains separate triangles), and numVertPerPart indicates |
170 | the number of vertex indices in the vertex array. |
171 | * non-polyhedra: number of parts (GL_TRIANGLE_STRIPs) to be drawn |
172 | separately (numParts calls to glDrawElements) to create the object. |
173 | numVertPerPart indicates the number of vertex indices to be |
174 | processed at each draw call. |
175 | * numParts * numVertPerPart gives the number of entries in the vertex |
176 | * array vertIdxs |
177 | */ |
178 | void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, |
179 | GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart) |
180 | { |
181 | GLint attribute_v_coord = fgStructure.CurrentWindow->Window.attribute_v_coord; |
182 | GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal; |
183 | GLint attribute_v_texture = fgStructure.CurrentWindow->Window.attribute_v_texture; |
184 | |
185 | if (fgStructure.CurrentWindow->State.VisualizeNormals) |
186 | /* generate normals for each vertex to be drawn as well */ |
187 | fghGenerateNormalVisualization(vertices, normals, numVertices); |
188 | |
189 | if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1)) |
190 | { |
191 | /* User requested a 2.0 draw */ |
192 | fghDrawGeometrySolid20(vertices, normals, textcs, numVertices, |
193 | vertIdxs, numParts, numVertIdxsPerPart, |
194 | attribute_v_coord, attribute_v_normal, attribute_v_texture); |
195 | |
196 | if (fgStructure.CurrentWindow->State.VisualizeNormals) |
197 | /* draw normals for each vertex as well */ |
198 | fghDrawNormalVisualization20(attribute_v_coord); |
199 | } |
200 | else |
201 | { |
202 | fghDrawGeometrySolid11(vertices, normals, textcs, numVertices, |
203 | vertIdxs, numParts, numVertIdxsPerPart); |
204 | |
205 | if (fgStructure.CurrentWindow->State.VisualizeNormals) |
206 | /* draw normals for each vertex as well */ |
207 | fghDrawNormalVisualization11(); |
208 | } |
209 | } |
210 | |
211 | |
212 | |
213 | /* Version for OpenGL (ES) 1.1 */ |
214 | static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals, |
215 | GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, |
216 | GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2 |
217 | ) |
218 | { |
219 | int i; |
220 | |
221 | glEnableClientState(GL_VERTEX_ARRAY); |
222 | glEnableClientState(GL_NORMAL_ARRAY); |
223 | |
224 | glVertexPointer(3, GL_FLOAT, 0, vertices); |
225 | glNormalPointer(GL_FLOAT, 0, normals); |
226 | |
227 | |
228 | if (!vertIdxs) |
229 | /* Draw per face (TODO: could use glMultiDrawArrays if available) */ |
230 | for (i=0; i<numParts; i++) |
231 | glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart); |
232 | else |
233 | for (i=0; i<numParts; i++) |
234 | glDrawElements(vertexMode,numVertPerPart,GL_UNSIGNED_SHORT,vertIdxs+i*numVertPerPart); |
235 | |
236 | if (vertIdxs2) |
237 | for (i=0; i<numParts2; i++) |
238 | glDrawElements(GL_LINE_LOOP,numVertPerPart2,GL_UNSIGNED_SHORT,vertIdxs2+i*numVertPerPart2); |
239 | |
240 | glDisableClientState(GL_VERTEX_ARRAY); |
241 | glDisableClientState(GL_NORMAL_ARRAY); |
242 | } |
243 | |
244 | |
245 | static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, |
246 | GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart) |
247 | { |
248 | int i; |
249 | |
250 | glEnableClientState(GL_VERTEX_ARRAY); |
251 | glEnableClientState(GL_NORMAL_ARRAY); |
252 | |
253 | glVertexPointer(3, GL_FLOAT, 0, vertices); |
254 | glNormalPointer(GL_FLOAT, 0, normals); |
255 | |
256 | if (textcs) |
257 | { |
258 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
259 | glTexCoordPointer(2, GL_FLOAT, 0, textcs); |
260 | } |
261 | |
262 | if (!vertIdxs) |
263 | glDrawArrays(GL_TRIANGLES, 0, numVertices); |
264 | else |
265 | if (numParts>1) |
266 | for (i=0; i<numParts; i++) |
267 | glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs+i*numVertIdxsPerPart); |
268 | else |
269 | glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs); |
270 | |
271 | glDisableClientState(GL_VERTEX_ARRAY); |
272 | glDisableClientState(GL_NORMAL_ARRAY); |
273 | if (textcs) |
274 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); |
275 | } |
276 | |
277 | /* Version for OpenGL (ES) >= 2.0 */ |
278 | static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices, |
279 | GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, |
280 | GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2, |
281 | GLint attribute_v_coord, GLint attribute_v_normal) |
282 | { |
283 | GLuint vbo_coords = 0, vbo_normals = 0, |
284 | ibo_elements = 0, ibo_elements2 = 0; |
285 | GLsizei numVertIdxs = numParts * numVertPerPart; |
286 | GLsizei numVertIdxs2 = numParts2 * numVertPerPart2; |
287 | int i; |
288 | |
289 | if (numVertices > 0 && attribute_v_coord != -1) { |
290 | fghGenBuffers(1, &vbo_coords); |
291 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); |
292 | fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]), |
293 | vertices, FGH_STATIC_DRAW); |
294 | } |
295 | |
296 | if (numVertices > 0 && attribute_v_normal != -1) { |
297 | fghGenBuffers(1, &vbo_normals); |
298 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); |
299 | fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]), |
300 | normals, FGH_STATIC_DRAW); |
301 | } |
302 | |
303 | if (vertIdxs != NULL) { |
304 | fghGenBuffers(1, &ibo_elements); |
305 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements); |
306 | fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]), |
307 | vertIdxs, FGH_STATIC_DRAW); |
308 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); |
309 | } |
310 | |
311 | if (vertIdxs2 != NULL) { |
312 | fghGenBuffers(1, &ibo_elements2); |
313 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2); |
314 | fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs2 * sizeof(vertIdxs2[0]), |
315 | vertIdxs2, FGH_STATIC_DRAW); |
316 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); |
317 | } |
318 | |
319 | if (vbo_coords) { |
320 | fghEnableVertexAttribArray(attribute_v_coord); |
321 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); |
322 | fghVertexAttribPointer( |
323 | attribute_v_coord, /* attribute */ |
324 | 3, /* number of elements per vertex, here (x,y,z) */ |
325 | GL_FLOAT, /* the type of each element */ |
326 | GL_FALSE, /* take our values as-is */ |
327 | 0, /* no extra data between each position */ |
328 | 0 /* offset of first element */ |
329 | ); |
330 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
331 | } |
332 | |
333 | if (vbo_normals) { |
334 | fghEnableVertexAttribArray(attribute_v_normal); |
335 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); |
336 | fghVertexAttribPointer( |
337 | attribute_v_normal, /* attribute */ |
338 | 3, /* number of elements per vertex, here (x,y,z) */ |
339 | GL_FLOAT, /* the type of each element */ |
340 | GL_FALSE, /* take our values as-is */ |
341 | 0, /* no extra data between each position */ |
342 | 0 /* offset of first element */ |
343 | ); |
344 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
345 | } |
346 | |
347 | if (!vertIdxs) { |
348 | /* Draw per face (TODO: could use glMultiDrawArrays if available) */ |
349 | for (i=0; i<numParts; i++) |
350 | glDrawArrays(vertexMode, i*numVertPerPart, numVertPerPart); |
351 | } else { |
352 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements); |
353 | for (i=0; i<numParts; i++) |
354 | glDrawElements(vertexMode, numVertPerPart, |
355 | GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertPerPart)); |
356 | /* Clean existing bindings before clean-up */ |
357 | /* Android showed instability otherwise */ |
358 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); |
359 | } |
360 | |
361 | if (vertIdxs2) { |
362 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2); |
363 | for (i=0; i<numParts2; i++) |
364 | glDrawElements(GL_LINE_LOOP, numVertPerPart2, |
365 | GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs2[0])*i*numVertPerPart2)); |
366 | /* Clean existing bindings before clean-up */ |
367 | /* Android showed instability otherwise */ |
368 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); |
369 | } |
370 | |
371 | if (vbo_coords != 0) |
372 | fghDisableVertexAttribArray(attribute_v_coord); |
373 | if (vbo_normals != 0) |
374 | fghDisableVertexAttribArray(attribute_v_normal); |
375 | |
376 | if (vbo_coords != 0) |
377 | fghDeleteBuffers(1, &vbo_coords); |
378 | if (vbo_normals != 0) |
379 | fghDeleteBuffers(1, &vbo_normals); |
380 | if (ibo_elements != 0) |
381 | fghDeleteBuffers(1, &ibo_elements); |
382 | if (ibo_elements2 != 0) |
383 | fghDeleteBuffers(1, &ibo_elements2); |
384 | } |
385 | |
386 | |
387 | |
388 | |
389 | /* Version for OpenGL (ES) >= 2.0 */ |
390 | static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, |
391 | GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart, |
392 | GLint attribute_v_coord, GLint attribute_v_normal, GLint attribute_v_texture) |
393 | { |
394 | GLuint vbo_coords = 0, vbo_normals = 0, vbo_textcs = 0, ibo_elements = 0; |
395 | GLsizei numVertIdxs = numParts * numVertIdxsPerPart; |
396 | int i; |
397 | |
398 | if (numVertices > 0 && attribute_v_coord != -1) { |
399 | fghGenBuffers(1, &vbo_coords); |
400 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); |
401 | fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]), |
402 | vertices, FGH_STATIC_DRAW); |
403 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
404 | } |
405 | |
406 | if (numVertices > 0 && attribute_v_normal != -1) { |
407 | fghGenBuffers(1, &vbo_normals); |
408 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); |
409 | fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]), |
410 | normals, FGH_STATIC_DRAW); |
411 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
412 | } |
413 | |
414 | if (numVertices > 0 && attribute_v_texture != -1 && textcs) { |
415 | fghGenBuffers(1, &vbo_textcs); |
416 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs); |
417 | fghBufferData(FGH_ARRAY_BUFFER, numVertices * 2 * sizeof(textcs[0]), |
418 | textcs, FGH_STATIC_DRAW); |
419 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
420 | } |
421 | |
422 | if (vertIdxs != NULL) { |
423 | fghGenBuffers(1, &ibo_elements); |
424 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements); |
425 | fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]), |
426 | vertIdxs, FGH_STATIC_DRAW); |
427 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); |
428 | } |
429 | |
430 | if (vbo_coords) { |
431 | fghEnableVertexAttribArray(attribute_v_coord); |
432 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); |
433 | fghVertexAttribPointer( |
434 | attribute_v_coord, /* attribute */ |
435 | 3, /* number of elements per vertex, here (x,y,z) */ |
436 | GL_FLOAT, /* the type of each element */ |
437 | GL_FALSE, /* take our values as-is */ |
438 | 0, /* no extra data between each position */ |
439 | 0 /* offset of first element */ |
440 | ); |
441 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
442 | }; |
443 | |
444 | if (vbo_normals) { |
445 | fghEnableVertexAttribArray(attribute_v_normal); |
446 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); |
447 | fghVertexAttribPointer( |
448 | attribute_v_normal, /* attribute */ |
449 | 3, /* number of elements per vertex, here (x,y,z) */ |
450 | GL_FLOAT, /* the type of each element */ |
451 | GL_FALSE, /* take our values as-is */ |
452 | 0, /* no extra data between each position */ |
453 | 0 /* offset of first element */ |
454 | ); |
455 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
456 | }; |
457 | |
458 | if (vbo_textcs) { |
459 | fghEnableVertexAttribArray(attribute_v_texture); |
460 | fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs); |
461 | fghVertexAttribPointer( |
462 | attribute_v_texture,/* attribute */ |
463 | 2, /* number of elements per vertex, here (s,t) */ |
464 | GL_FLOAT, /* the type of each element */ |
465 | GL_FALSE, /* take our values as-is */ |
466 | 0, /* no extra data between each position */ |
467 | 0 /* offset of first element */ |
468 | ); |
469 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
470 | }; |
471 | |
472 | if (vertIdxs == NULL) { |
473 | glDrawArrays(GL_TRIANGLES, 0, numVertices); |
474 | } else { |
475 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements); |
476 | if (numParts>1) { |
477 | for (i=0; i<numParts; i++) { |
478 | glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(vertIdxs[0])*i*numVertIdxsPerPart)); |
479 | } |
480 | } else { |
481 | glDrawElements(GL_TRIANGLES, numVertIdxsPerPart, GL_UNSIGNED_SHORT, 0); |
482 | } |
483 | /* Clean existing bindings before clean-up */ |
484 | /* Android showed instability otherwise */ |
485 | fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); |
486 | } |
487 | |
488 | if (vbo_coords != 0) |
489 | fghDisableVertexAttribArray(attribute_v_coord); |
490 | if (vbo_normals != 0) |
491 | fghDisableVertexAttribArray(attribute_v_normal); |
492 | if (vbo_textcs != 0) |
493 | fghDisableVertexAttribArray(attribute_v_texture); |
494 | |
495 | if (vbo_coords != 0) |
496 | fghDeleteBuffers(1, &vbo_coords); |
497 | if (vbo_normals != 0) |
498 | fghDeleteBuffers(1, &vbo_normals); |
499 | if (vbo_textcs != 0) |
500 | fghDeleteBuffers(1, &vbo_textcs); |
501 | if (ibo_elements != 0) |
502 | fghDeleteBuffers(1, &ibo_elements); |
503 | } |
504 | |
505 | |
506 | |
507 | /** |
508 | * Generate vertex indices for visualizing the normals. |
509 | * vertices are written into verticesForNormalVisualization. |
510 | * This must be freed by caller, we do the free at the |
511 | * end of fghDrawNormalVisualization11/fghDrawNormalVisualization20 |
512 | */ |
513 | static GLfloat *verticesForNormalVisualization; |
514 | static GLsizei numNormalVertices = 0; |
515 | static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices) |
516 | { |
517 | int i,j; |
518 | numNormalVertices = numVertices * 2; |
519 | verticesForNormalVisualization = malloc(numNormalVertices*3 * sizeof(GLfloat)); |
520 | |
521 | for (i=0,j=0; i<numNormalVertices*3/2; i+=3, j+=6) |
522 | { |
523 | verticesForNormalVisualization[j+0] = vertices[i+0]; |
524 | verticesForNormalVisualization[j+1] = vertices[i+1]; |
525 | verticesForNormalVisualization[j+2] = vertices[i+2]; |
526 | verticesForNormalVisualization[j+3] = vertices[i+0] + normals[i+0]/4.f; |
527 | verticesForNormalVisualization[j+4] = vertices[i+1] + normals[i+1]/4.f; |
528 | verticesForNormalVisualization[j+5] = vertices[i+2] + normals[i+2]/4.f; |
529 | } |
530 | } |
531 | |
532 | /* Version for OpenGL (ES) 1.1 */ |
533 | static void fghDrawNormalVisualization11() |
534 | { |
535 | GLfloat currentColor[4]; |
536 | /* Setup draw color: (1,1,1)-shape's color */ |
537 | glGetFloatv(GL_CURRENT_COLOR,currentColor); |
538 | glColor4f(1-currentColor[0],1-currentColor[1],1-currentColor[2],currentColor[3]); |
539 | |
540 | glEnableClientState(GL_VERTEX_ARRAY); |
541 | |
542 | glVertexPointer(3, GL_FLOAT, 0, verticesForNormalVisualization); |
543 | glDrawArrays(GL_LINES, 0, numNormalVertices); |
544 | |
545 | glDisableClientState(GL_VERTEX_ARRAY); |
546 | |
547 | /* Done, free memory, reset color */ |
548 | free(verticesForNormalVisualization); |
549 | glColor4f(currentColor[0],currentColor[1],currentColor[2],currentColor[3]); |
550 | } |
551 | |
552 | /* Version for OpenGL (ES) >= 2.0 */ |
553 | static void fghDrawNormalVisualization20(GLint attribute_v_coord) |
554 | { |
555 | GLuint vbo_coords = 0; |
556 | |
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); |
562 | } |
563 | |
564 | |
565 | if (vbo_coords) { |
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 */ |
575 | ); |
576 | fghBindBuffer(FGH_ARRAY_BUFFER, 0); |
577 | } |
578 | |
579 | glDrawArrays(GL_LINES, 0, numNormalVertices); |
580 | |
581 | if (vbo_coords != 0) |
582 | fghDisableVertexAttribArray(attribute_v_coord); |
583 | |
584 | if (vbo_coords != 0) |
585 | fghDeleteBuffers(1, &vbo_coords); |
586 | |
587 | /* Done, free memory */ |
588 | free(verticesForNormalVisualization); |
589 | } |
590 | |
591 | /** |
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... |
599 | */ |
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) */ |
602 | |
603 | static void fghGenerateGeometryWithIndexArray(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut, GLushort *vertIdxOut) |
604 | { |
605 | int i,j,numEdgeIdxPerFace; |
606 | GLubyte *vertSamps = NULL; |
607 | switch (numEdgePerFace) |
608 | { |
609 | case 3: |
610 | /* nothing to do here, we'll draw with glDrawArrays */ |
611 | break; |
612 | case 4: |
613 | vertSamps = vert4Decomp; |
614 | numEdgeIdxPerFace = 6; /* 6 output vertices for each face */ |
615 | break; |
616 | case 5: |
617 | vertSamps = vert5Decomp; |
618 | numEdgeIdxPerFace = 9; /* 9 output vertices for each face */ |
619 | break; |
620 | } |
621 | /* |
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. |
625 | */ |
626 | for (i=0; i<numFaces; i++) |
627 | { |
628 | int normIdx = i*3; |
629 | int faceIdxVertIdx = i*numEdgePerFace; /* index to first element of "row" in vertex indices */ |
630 | for (j=0; j<numEdgePerFace; j++) |
631 | { |
632 | int outIdx = i*numEdgePerFace*3+j*3; |
633 | int vertIdx = vertIndices[faceIdxVertIdx+j]*3; |
634 | |
635 | vertOut[outIdx ] = vertices[vertIdx ]; |
636 | vertOut[outIdx+1] = vertices[vertIdx+1]; |
637 | vertOut[outIdx+2] = vertices[vertIdx+2]; |
638 | |
639 | normOut[outIdx ] = normals [normIdx ]; |
640 | normOut[outIdx+1] = normals [normIdx+1]; |
641 | normOut[outIdx+2] = normals [normIdx+2]; |
642 | } |
643 | |
644 | /* generate vertex indices for each face */ |
645 | if (vertSamps) |
646 | for (j=0; j<numEdgeIdxPerFace; j++) |
647 | vertIdxOut[i*numEdgeIdxPerFace+j] = faceIdxVertIdx + vertSamps[j]; |
648 | } |
649 | } |
650 | |
651 | static void fghGenerateGeometry(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut) |
652 | { |
653 | /* This function does the same as fghGenerateGeometryWithIndexArray, just skipping the index array generation... */ |
654 | fghGenerateGeometryWithIndexArray(numFaces, numEdgePerFace, vertices, vertIndices, normals, vertOut, normOut, NULL); |
655 | } |
656 | |
657 | |
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. |
664 | */ |
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()\ |
670 | {\ |
671 | fghGenerateGeometry(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\ |
672 | name##_v, name##_vi, name##_n,\ |
673 | name##_verts, name##_norms);\ |
674 | } |
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()\ |
681 | {\ |
682 | fghGenerateGeometryWithIndexArray(nameCaps##_NUM_FACES, nameCaps##_NUM_EDGE_PER_FACE,\ |
683 | name##_v, name##_vi, name##_n,\ |
684 | name##_verts, name##_norms, name##_vertIdxs);\ |
685 | } |
686 | |
687 | /* -- Cube -- */ |
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] = |
696 | { |
697 | .5f, .5f, .5f, |
698 | -.5f, .5f, .5f, |
699 | -.5f,-.5f, .5f, |
700 | .5f,-.5f, .5f, |
701 | .5f,-.5f,-.5f, |
702 | .5f, .5f,-.5f, |
703 | -.5f, .5f,-.5f, |
704 | -.5f,-.5f,-.5f |
705 | }; |
706 | /* Normal Vectors */ |
707 | static GLfloat cube_n[CUBE_NUM_FACES*3] = |
708 | { |
709 | 0.0f, 0.0f, 1.0f, |
710 | 1.0f, 0.0f, 0.0f, |
711 | 0.0f, 1.0f, 0.0f, |
712 | -1.0f, 0.0f, 0.0f, |
713 | 0.0f,-1.0f, 0.0f, |
714 | 0.0f, 0.0f,-1.0f |
715 | }; |
716 | |
717 | /* Vertex indices, as quads, before triangulation */ |
718 | static GLubyte cube_vi[CUBE_VERT_PER_OBJ] = |
719 | { |
720 | 0,1,2,3, |
721 | 0,3,4,5, |
722 | 0,5,6,1, |
723 | 1,6,7,2, |
724 | 7,4,3,2, |
725 | 4,7,6,5 |
726 | }; |
727 | DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(cube,Cube,CUBE) |
728 | |
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. |
735 | */ |
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] = |
744 | { |
745 | 0.0f, 1.61803398875f, 0.61803398875f, |
746 | - 1.0f, 1.0f, 1.0f, |
747 | -0.61803398875f, 0.0f, 1.61803398875f, |
748 | 0.61803398875f, 0.0f, 1.61803398875f, |
749 | 1.0f, 1.0f, 1.0f, |
750 | 0.0f, 1.61803398875f, -0.61803398875f, |
751 | 1.0f, 1.0f, - 1.0f, |
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, |
756 | 1.0f, - 1.0f, 1.0f, |
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 |
765 | }; |
766 | /* Normal Vectors */ |
767 | static GLfloat dodecahedron_n[DODECAHEDRON_NUM_FACES*3] = |
768 | { |
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, |
773 | |
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, |
778 | |
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, |
783 | }; |
784 | |
785 | /* Vertex indices */ |
786 | static GLubyte dodecahedron_vi[DODECAHEDRON_VERT_PER_OBJ] = |
787 | { |
788 | 0, 1, 2, 3, 4, |
789 | 5, 6, 7, 8, 9, |
790 | 10, 11, 3, 2, 12, |
791 | 13, 14, 8, 7, 15, |
792 | |
793 | 3, 11, 16, 17, 4, |
794 | 2, 1, 18, 19, 12, |
795 | 7, 6, 17, 16, 15, |
796 | 8, 14, 19, 18, 9, |
797 | |
798 | 17, 6, 5, 0, 4, |
799 | 16, 11, 10, 13, 15, |
800 | 18, 1, 0, 5, 9, |
801 | 19, 14, 13, 10, 12 |
802 | }; |
803 | DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(dodecahedron,Dodecahedron,DODECAHEDRON) |
804 | |
805 | |
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] = |
815 | { |
816 | 1.0f, 0.0f, 0.0f, |
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, |
827 | - 1.0f, 0.0f, 0.0f |
828 | }; |
829 | /* Normal Vectors: |
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] ) ; |
833 | */ |
834 | static GLfloat icosahedron_n[ICOSAHEDRON_NUM_FACES*3] = |
835 | { |
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, |
856 | }; |
857 | |
858 | /* Vertex indices */ |
859 | static GLubyte icosahedron_vi[ICOSAHEDRON_VERT_PER_OBJ] = |
860 | { |
861 | 0, 1, 2 , |
862 | 0, 2, 3 , |
863 | 0, 3, 4 , |
864 | 0, 4, 5 , |
865 | 0, 5, 1 , |
866 | 1, 8, 2 , |
867 | 2, 7, 3 , |
868 | 3, 6, 4 , |
869 | 4, 10, 5 , |
870 | 5, 9, 1 , |
871 | 1, 9, 8 , |
872 | 2, 8, 7 , |
873 | 3, 7, 6 , |
874 | 4, 6, 10 , |
875 | 5, 10, 9 , |
876 | 11, 9, 10 , |
877 | 11, 8, 9 , |
878 | 11, 7, 8 , |
879 | 11, 6, 7 , |
880 | 11, 10, 6 |
881 | }; |
882 | DECLARE_SHAPE_CACHE(icosahedron,Icosahedron,ICOSAHEDRON) |
883 | |
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 |
891 | |
892 | /* Vertex Coordinates */ |
893 | static GLfloat octahedron_v[OCTAHEDRON_NUM_VERT*3] = |
894 | { |
895 | 1.f, 0.f, 0.f, |
896 | 0.f, 1.f, 0.f, |
897 | 0.f, 0.f, 1.f, |
898 | -1.f, 0.f, 0.f, |
899 | 0.f, -1.f, 0.f, |
900 | 0.f, 0.f, -1.f, |
901 | |
902 | }; |
903 | /* Normal Vectors */ |
904 | static GLfloat octahedron_n[OCTAHEDRON_NUM_FACES*3] = |
905 | { |
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 |
914 | |
915 | }; |
916 | |
917 | /* Vertex indices */ |
918 | static GLubyte octahedron_vi[OCTAHEDRON_VERT_PER_OBJ] = |
919 | { |
920 | 0, 1, 2, |
921 | 0, 5, 1, |
922 | 0, 2, 4, |
923 | 0, 4, 5, |
924 | 3, 2, 1, |
925 | 3, 1, 5, |
926 | 3, 4, 2, |
927 | 3, 5, 4 |
928 | }; |
929 | DECLARE_SHAPE_CACHE(octahedron,Octahedron,OCTAHEDRON) |
930 | |
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 */ |
938 | |
939 | /* Vertex Coordinates */ |
940 | static GLfloat rhombicdodecahedron_v[RHOMBICDODECAHEDRON_NUM_VERT*3] = |
941 | { |
942 | 0.0f, 0.0f, 1.0f, |
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, |
955 | 0.0f, 0.0f, -1.0f |
956 | }; |
957 | /* Normal Vectors */ |
958 | static GLfloat rhombicdodecahedron_n[RHOMBICDODECAHEDRON_NUM_FACES*3] = |
959 | { |
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, |
964 | 0.0f, 1.0f, 0.0f, |
965 | - 1.0f, 0.0f, 0.0f, |
966 | 0.0f, - 1.0f, 0.0f, |
967 | 1.0f, 0.0f, 0.0f, |
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 |
972 | }; |
973 | |
974 | /* Vertex indices */ |
975 | static GLubyte rhombicdodecahedron_vi[RHOMBICDODECAHEDRON_VERT_PER_OBJ] = |
976 | { |
977 | 0, 1, 5, 2, |
978 | 0, 2, 6, 3, |
979 | 0, 3, 7, 4, |
980 | 0, 4, 8, 1, |
981 | 5, 10, 6, 2, |
982 | 6, 11, 7, 3, |
983 | 7, 12, 8, 4, |
984 | 8, 9, 5, 1, |
985 | 5, 9, 13, 10, |
986 | 6, 10, 13, 11, |
987 | 7, 11, 13, 12, |
988 | 8, 12, 13, 9 |
989 | }; |
990 | DECLARE_SHAPE_CACHE_DECOMPOSE_TO_TRIANGLE(rhombicdodecahedron,RhombicDodecahedron,RHOMBICDODECAHEDRON) |
991 | |
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 |
999 | * |
1000 | * Normals: The unit normals are simply the negative of the coordinates of the point not on the surface. |
1001 | */ |
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 |
1008 | |
1009 | /* Vertex Coordinates */ |
1010 | static GLfloat tetrahedron_v[TETRAHEDRON_NUM_VERT*3] = |
1011 | { |
1012 | 1.0f, 0.0f, 0.0f, |
1013 | -0.333333333333f, 0.942809041582f, 0.0f, |
1014 | -0.333333333333f, -0.471404520791f, 0.816496580928f, |
1015 | -0.333333333333f, -0.471404520791f, -0.816496580928f |
1016 | }; |
1017 | /* Normal Vectors */ |
1018 | static GLfloat tetrahedron_n[TETRAHEDRON_NUM_FACES*3] = |
1019 | { |
1020 | - 1.0f, 0.0f, 0.0f, |
1021 | 0.333333333333f, -0.942809041582f, 0.0f, |
1022 | 0.333333333333f, 0.471404520791f, -0.816496580928f, |
1023 | 0.333333333333f, 0.471404520791f, 0.816496580928f |
1024 | }; |
1025 | |
1026 | /* Vertex indices */ |
1027 | static GLubyte tetrahedron_vi[TETRAHEDRON_VERT_PER_OBJ] = |
1028 | { |
1029 | 1, 3, 2, |
1030 | 0, 2, 3, |
1031 | 0, 3, 1, |
1032 | 0, 1, 2 |
1033 | }; |
1034 | DECLARE_SHAPE_CACHE(tetrahedron,Tetrahedron,TETRAHEDRON) |
1035 | |
1036 | /* -- Sierpinski Sponge -- */ |
1037 | static unsigned int ipow (int x, unsigned int y) |
1038 | { |
1039 | /* return y==0? 1: y==1? x: (y%2? x: 1) * ipow(x*x, y/2); */ |
1040 | if (y==0) |
1041 | return 1; |
1042 | else |
1043 | { |
1044 | if (y==1) |
1045 | return x; |
1046 | else |
1047 | { |
1048 | return (y%2? x: 1) * ipow(x*x, y/2); |
1049 | } |
1050 | } |
1051 | } |
1052 | |
1053 | static void fghSierpinskiSpongeGenerate ( int numLevels, double offset[3], GLfloat scale, GLfloat* vertices, GLfloat* normals ) |
1054 | { |
1055 | int i, j; |
1056 | if ( numLevels == 0 ) |
1057 | { |
1058 | for (i=0; i<TETRAHEDRON_NUM_FACES; i++) |
1059 | { |
1060 | int normIdx = i*3; |
1061 | int faceIdxVertIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE; |
1062 | for (j=0; j<TETRAHEDRON_NUM_EDGE_PER_FACE; j++) |
1063 | { |
1064 | int outIdx = i*TETRAHEDRON_NUM_EDGE_PER_FACE*3+j*3; |
1065 | int vertIdx = tetrahedron_vi[faceIdxVertIdx+j]*3; |
1066 | |
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]; |
1070 | |
1071 | normals [outIdx ] = tetrahedron_n[normIdx ]; |
1072 | normals [outIdx+1] = tetrahedron_n[normIdx+1]; |
1073 | normals [outIdx+2] = tetrahedron_n[normIdx+2]; |
1074 | } |
1075 | } |
1076 | } |
1077 | else if ( numLevels > 0 ) |
1078 | { |
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; |
1081 | scale /= 2.0 ; |
1082 | for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ ) |
1083 | { |
1084 | int idx = i*3; |
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 ); |
1089 | } |
1090 | } |
1091 | } |
1092 | |
1093 | /* -- Now the various non-polyhedra (shapes involving circles) -- */ |
1094 | /* |
1095 | * Compute lookup table of cos and sin values forming a circle |
1096 | * (or half circle if halfCircle==TRUE) |
1097 | * |
1098 | * Notes: |
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 |
1103 | */ |
1104 | static void fghCircleTable(GLfloat **sint, GLfloat **cost, const int n, const GLboolean halfCircle) |
1105 | { |
1106 | int i; |
1107 | |
1108 | /* Table size, the sign of n flips the circle direction */ |
1109 | const int size = abs(n); |
1110 | |
1111 | /* Determine the angle between samples */ |
1112 | const GLfloat angle = (halfCircle?1:2)*(GLfloat)M_PI/(GLfloat)( ( n == 0 ) ? 1 : n ); |
1113 | |
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)); |
1117 | |
1118 | /* Bail out if memory allocation fails, fgError never returns */ |
1119 | if (!(*sint) || !(*cost)) |
1120 | { |
1121 | free(*sint); |
1122 | free(*cost); |
1123 | fgError("Failed to allocate memory in fghCircleTable" ); |
1124 | } |
1125 | |
1126 | /* Compute cos and sin around the circle */ |
1127 | (*sint)[0] = 0.0; |
1128 | (*cost)[0] = 1.0; |
1129 | |
1130 | for (i=1; i<size; i++) |
1131 | { |
1132 | (*sint)[i] = (GLfloat)sin(angle*i); |
1133 | (*cost)[i] = (GLfloat)cos(angle*i); |
1134 | } |
1135 | |
1136 | |
1137 | if (halfCircle) |
1138 | { |
1139 | (*sint)[size] = 0.0f; /* sin PI */ |
1140 | (*cost)[size] = -1.0f; /* cos PI */ |
1141 | } |
1142 | else |
1143 | { |
1144 | /* Last sample is duplicate of the first (sin or cos of 2 PI) */ |
1145 | (*sint)[size] = (*sint)[0]; |
1146 | (*cost)[size] = (*cost)[0]; |
1147 | } |
1148 | } |
1149 | |
1150 | static void fghGenerateSphere(GLfloat radius, GLint slices, GLint stacks, GLfloat **vertices, GLfloat **normals, int* nVert) |
1151 | { |
1152 | int i,j; |
1153 | int idx = 0; /* idx into vertex/normal buffer */ |
1154 | GLfloat x,y,z; |
1155 | |
1156 | /* Pre-computed circle */ |
1157 | GLfloat *sint1,*cost1; |
1158 | GLfloat *sint2,*cost2; |
1159 | |
1160 | /* number of unique vertices */ |
1161 | if (slices==0 || stacks<2) |
1162 | { |
1163 | /* nothing to generate */ |
1164 | *nVert = 0; |
1165 | return; |
1166 | } |
1167 | *nVert = slices*(stacks-1)+2; |
1168 | if ((*nVert) > 65535) |
1169 | /* |
1170 | * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above |
1171 | */ |
1172 | fgWarning("fghGenerateSphere: too many slices or stacks requested, indices will wrap" ); |
1173 | |
1174 | /* precompute values on unit circle */ |
1175 | fghCircleTable(&sint1,&cost1,-slices,GL_FALSE); |
1176 | fghCircleTable(&sint2,&cost2, stacks,GL_TRUE); |
1177 | |
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)) |
1182 | { |
1183 | free(*vertices); |
1184 | free(*normals); |
1185 | fgError("Failed to allocate memory in fghGenerateSphere" ); |
1186 | } |
1187 | |
1188 | /* top */ |
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; |
1195 | idx = 3; |
1196 | |
1197 | /* each stack */ |
1198 | for( i=1; i<stacks; i++ ) |
1199 | { |
1200 | for(j=0; j<slices; j++, idx+=3) |
1201 | { |
1202 | x = cost1[j]*sint2[i]; |
1203 | y = sint1[j]*sint2[i]; |
1204 | z = cost2[i]; |
1205 | |
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; |
1212 | } |
1213 | } |
1214 | |
1215 | /* bottom */ |
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; |
1222 | |
1223 | /* Done creating vertices, release sin and cos tables */ |
1224 | free(sint1); |
1225 | free(cost1); |
1226 | free(sint2); |
1227 | free(cost2); |
1228 | } |
1229 | |
1230 | void fghGenerateCone( |
1231 | GLfloat base, GLfloat height, GLint slices, GLint stacks, /* input */ |
1232 | GLfloat **vertices, GLfloat **normals, int* nVert /* output */ |
1233 | ) |
1234 | { |
1235 | int i,j; |
1236 | int idx = 0; /* idx into vertex/normal buffer */ |
1237 | |
1238 | /* Pre-computed circle */ |
1239 | GLfloat *sint,*cost; |
1240 | |
1241 | /* Step in z and radius as stacks are drawn. */ |
1242 | GLfloat z = 0; |
1243 | GLfloat r = (GLfloat)base; |
1244 | |
1245 | const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 ); |
1246 | const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 ); |
1247 | |
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 )); |
1251 | |
1252 | |
1253 | |
1254 | /* number of unique vertices */ |
1255 | if (slices==0 || stacks<1) |
1256 | { |
1257 | /* nothing to generate */ |
1258 | *nVert = 0; |
1259 | return; |
1260 | } |
1261 | *nVert = slices*(stacks+2)+1; /* need an extra stack for closing off bottom with correct normals */ |
1262 | |
1263 | if ((*nVert) > 65535) |
1264 | /* |
1265 | * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above |
1266 | */ |
1267 | fgWarning("fghGenerateCone: too many slices or stacks requested, indices will wrap" ); |
1268 | |
1269 | /* Pre-computed circle */ |
1270 | fghCircleTable(&sint,&cost,-slices,GL_FALSE); |
1271 | |
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)) |
1276 | { |
1277 | free(*vertices); |
1278 | free(*normals); |
1279 | fgError("Failed to allocate memory in fghGenerateCone" ); |
1280 | } |
1281 | |
1282 | /* bottom */ |
1283 | (*vertices)[0] = 0.f; |
1284 | (*vertices)[1] = 0.f; |
1285 | (*vertices)[2] = z; |
1286 | (*normals )[0] = 0.f; |
1287 | (*normals )[1] = 0.f; |
1288 | (*normals )[2] = -1.f; |
1289 | idx = 3; |
1290 | /* other on bottom (get normals right) */ |
1291 | for (j=0; j<slices; j++, idx+=3) |
1292 | { |
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; |
1299 | } |
1300 | |
1301 | /* each stack */ |
1302 | for (i=0; i<stacks+1; i++ ) |
1303 | { |
1304 | for (j=0; j<slices; j++, idx+=3) |
1305 | { |
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; |
1312 | } |
1313 | |
1314 | z += zStep; |
1315 | r -= rStep; |
1316 | } |
1317 | |
1318 | /* Release sin and cos tables */ |
1319 | free(sint); |
1320 | free(cost); |
1321 | } |
1322 | |
1323 | void fghGenerateCylinder( |
1324 | GLfloat radius, GLfloat height, GLint slices, GLint stacks, /* input */ |
1325 | GLfloat **vertices, GLfloat **normals, int* nVert /* output */ |
1326 | ) |
1327 | { |
1328 | int i,j; |
1329 | int idx = 0; /* idx into vertex/normal buffer */ |
1330 | |
1331 | /* Step in z as stacks are drawn. */ |
1332 | GLfloat radf = (GLfloat)radius; |
1333 | GLfloat z; |
1334 | const GLfloat zStep = (GLfloat)height / ( ( stacks > 0 ) ? stacks : 1 ); |
1335 | |
1336 | /* Pre-computed circle */ |
1337 | GLfloat *sint,*cost; |
1338 | |
1339 | /* number of unique vertices */ |
1340 | if (slices==0 || stacks<1) |
1341 | { |
1342 | /* nothing to generate */ |
1343 | *nVert = 0; |
1344 | return; |
1345 | } |
1346 | *nVert = slices*(stacks+3)+2; /* need two extra stacks for closing off top and bottom with correct normals */ |
1347 | |
1348 | if ((*nVert) > 65535) |
1349 | /* |
1350 | * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above |
1351 | */ |
1352 | fgWarning("fghGenerateCylinder: too many slices or stacks requested, indices will wrap" ); |
1353 | |
1354 | /* Pre-computed circle */ |
1355 | fghCircleTable(&sint,&cost,-slices,GL_FALSE); |
1356 | |
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)) |
1361 | { |
1362 | free(*vertices); |
1363 | free(*normals); |
1364 | fgError("Failed to allocate memory in fghGenerateCylinder" ); |
1365 | } |
1366 | |
1367 | z=0; |
1368 | /* top on Z-axis */ |
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; |
1375 | idx = 3; |
1376 | /* other on top (get normals right) */ |
1377 | for (j=0; j<slices; j++, idx+=3) |
1378 | { |
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; |
1385 | } |
1386 | |
1387 | /* each stack */ |
1388 | for (i=0; i<stacks+1; i++ ) |
1389 | { |
1390 | for (j=0; j<slices; j++, idx+=3) |
1391 | { |
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; |
1398 | } |
1399 | |
1400 | z += zStep; |
1401 | } |
1402 | |
1403 | /* other on bottom (get normals right) */ |
1404 | z -= zStep; |
1405 | for (j=0; j<slices; j++, idx+=3) |
1406 | { |
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; |
1413 | } |
1414 | |
1415 | /* bottom */ |
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; |
1422 | |
1423 | /* Release sin and cos tables */ |
1424 | free(sint); |
1425 | free(cost); |
1426 | } |
1427 | |
1428 | void fghGenerateTorus( |
1429 | double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings, /* input */ |
1430 | GLfloat **vertices, GLfloat **normals, int* nVert /* output */ |
1431 | ) |
1432 | { |
1433 | GLfloat iradius = (float)dInnerRadius; |
1434 | GLfloat oradius = (float)dOuterRadius; |
1435 | int i, j; |
1436 | |
1437 | /* Pre-computed circle */ |
1438 | GLfloat *spsi, *cpsi; |
1439 | GLfloat *sphi, *cphi; |
1440 | |
1441 | /* number of unique vertices */ |
1442 | if (nSides<2 || nRings<2) |
1443 | { |
1444 | /* nothing to generate */ |
1445 | *nVert = 0; |
1446 | return; |
1447 | } |
1448 | *nVert = nSides * nRings; |
1449 | |
1450 | if ((*nVert) > 65535) |
1451 | /* |
1452 | * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above |
1453 | */ |
1454 | fgWarning("fghGenerateTorus: too many slices or stacks requested, indices will wrap" ); |
1455 | |
1456 | /* precompute values on unit circle */ |
1457 | fghCircleTable(&spsi,&cpsi, nRings,GL_FALSE); |
1458 | fghCircleTable(&sphi,&cphi,-nSides,GL_FALSE); |
1459 | |
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)) |
1464 | { |
1465 | free(*vertices); |
1466 | free(*normals); |
1467 | fgError("Failed to allocate memory in fghGenerateTorus" ); |
1468 | } |
1469 | |
1470 | for( j=0; j<nRings; j++ ) |
1471 | { |
1472 | for( i=0; i<nSides; i++ ) |
1473 | { |
1474 | int offset = 3 * ( j * nSides + i ) ; |
1475 | |
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] ; |
1482 | } |
1483 | } |
1484 | |
1485 | /* Release sin and cos tables */ |
1486 | free(spsi); |
1487 | free(cpsi); |
1488 | free(sphi); |
1489 | free(cphi); |
1490 | } |
1491 | |
1492 | /* -- INTERNAL DRAWING functions --------------------------------------- */ |
1493 | #define _DECLARE_INTERNAL_DRAW_DO_DECLARE(name,nameICaps,nameCaps,vertIdxs)\ |
1494 | static void fgh##nameICaps( GLboolean useWireMode )\ |
1495 | {\ |
1496 | if (!name##Cached)\ |
1497 | {\ |
1498 | fgh##nameICaps##Generate();\ |
1499 | name##Cached = GL_TRUE;\ |
1500 | }\ |
1501 | \ |
1502 | if (useWireMode)\ |
1503 | {\ |
1504 | fghDrawGeometryWire (name##_verts,name##_norms,nameCaps##_VERT_PER_OBJ, \ |
1505 | NULL,nameCaps##_NUM_FACES,nameCaps##_NUM_EDGE_PER_FACE,GL_LINE_LOOP,\ |
1506 | NULL,0,0);\ |
1507 | }\ |
1508 | else\ |
1509 | {\ |
1510 | fghDrawGeometrySolid(name##_verts,name##_norms,NULL,nameCaps##_VERT_PER_OBJ,\ |
1511 | vertIdxs, 1, nameCaps##_VERT_PER_OBJ_TRI); \ |
1512 | }\ |
1513 | } |
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) |
1516 | |
1517 | static void fghCube( GLfloat dSize, GLboolean useWireMode ) |
1518 | { |
1519 | GLfloat *vertices; |
1520 | |
1521 | if (!cubeCached) |
1522 | { |
1523 | fghCubeGenerate(); |
1524 | cubeCached = GL_TRUE; |
1525 | } |
1526 | |
1527 | if (dSize!=1.f) |
1528 | { |
1529 | /* Need to build new vertex list containing vertices for cube of different size */ |
1530 | int i; |
1531 | |
1532 | vertices = malloc(CUBE_VERT_ELEM_PER_OBJ * sizeof(GLfloat)); |
1533 | |
1534 | /* Bail out if memory allocation fails, fgError never returns */ |
1535 | if (!vertices) |
1536 | { |
1537 | free(vertices); |
1538 | fgError("Failed to allocate memory in fghCube" ); |
1539 | } |
1540 | |
1541 | for (i=0; i<CUBE_VERT_ELEM_PER_OBJ; i++) |
1542 | vertices[i] = dSize*cube_verts[i]; |
1543 | } |
1544 | else |
1545 | vertices = cube_verts; |
1546 | |
1547 | if (useWireMode) |
1548 | fghDrawGeometryWire(vertices, cube_norms, CUBE_VERT_PER_OBJ, |
1549 | NULL,CUBE_NUM_FACES, CUBE_NUM_EDGE_PER_FACE,GL_LINE_LOOP, |
1550 | NULL,0,0); |
1551 | else |
1552 | fghDrawGeometrySolid(vertices, cube_norms, NULL, CUBE_VERT_PER_OBJ, |
1553 | cube_vertIdxs, 1, CUBE_VERT_PER_OBJ_TRI); |
1554 | |
1555 | if (dSize!=1.f) |
1556 | /* cleanup allocated memory */ |
1557 | free(vertices); |
1558 | } |
1559 | |
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) |
1565 | |
1566 | static void fghSierpinskiSponge ( int numLevels, double offset[3], GLfloat scale, GLboolean useWireMode ) |
1567 | { |
1568 | GLfloat *vertices; |
1569 | GLfloat * normals; |
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; |
1573 | |
1574 | if (numTetr) |
1575 | { |
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) |
1581 | { |
1582 | free(vertices); |
1583 | free(normals); |
1584 | fgError("Failed to allocate memory in fghSierpinskiSponge" ); |
1585 | } |
1586 | |
1587 | /* Generate elements */ |
1588 | fghSierpinskiSpongeGenerate ( numLevels, offset, scale, vertices, normals ); |
1589 | |
1590 | /* Draw and cleanup */ |
1591 | if (useWireMode) |
1592 | fghDrawGeometryWire (vertices,normals,numVert, |
1593 | NULL,numFace,TETRAHEDRON_NUM_EDGE_PER_FACE,GL_LINE_LOOP, |
1594 | NULL,0,0); |
1595 | else |
1596 | fghDrawGeometrySolid(vertices,normals,NULL,numVert,NULL,1,0); |
1597 | |
1598 | free(vertices); |
1599 | free(normals ); |
1600 | } |
1601 | } |
1602 | |
1603 | |
1604 | static void fghSphere( GLfloat radius, GLint slices, GLint stacks, GLboolean useWireMode ) |
1605 | { |
1606 | int i,j,idx, nVert; |
1607 | GLfloat *vertices, *normals; |
1608 | |
1609 | /* Generate vertices and normals */ |
1610 | fghGenerateSphere(radius,slices,stacks,&vertices,&normals,&nVert); |
1611 | |
1612 | if (nVert==0) |
1613 | /* nothing to draw */ |
1614 | return; |
1615 | |
1616 | if (useWireMode) |
1617 | { |
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. |
1622 | */ |
1623 | |
1624 | sliceIdx = malloc(slices*(stacks+1)*sizeof(GLushort)); |
1625 | stackIdx = malloc(slices*(stacks-1)*sizeof(GLushort)); |
1626 | if (!(stackIdx) || !(sliceIdx)) |
1627 | { |
1628 | free(stackIdx); |
1629 | free(sliceIdx); |
1630 | fgError("Failed to allocate memory in fghSphere" ); |
1631 | } |
1632 | |
1633 | /* generate for each stack */ |
1634 | for (i=0,idx=0; i<stacks-1; i++) |
1635 | { |
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++) |
1638 | { |
1639 | stackIdx[idx] = offset+j; |
1640 | } |
1641 | } |
1642 | |
1643 | /* generate for each slice */ |
1644 | for (i=0,idx=0; i<slices; i++) |
1645 | { |
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++) |
1649 | { |
1650 | sliceIdx[idx] = offset+j*slices; |
1651 | } |
1652 | sliceIdx[idx++] = nVert-1; /* zero based index, last element in array... */ |
1653 | } |
1654 | |
1655 | /* draw */ |
1656 | fghDrawGeometryWire(vertices,normals,nVert, |
1657 | sliceIdx,slices,stacks+1,GL_LINE_STRIP, |
1658 | stackIdx,stacks-1,slices); |
1659 | |
1660 | /* cleanup allocated memory */ |
1661 | free(sliceIdx); |
1662 | free(stackIdx); |
1663 | } |
1664 | else |
1665 | { |
1666 | /* First, generate vertex index arrays for drawing with glDrawElements |
1667 | * All stacks, including top and bottom are covered with a triangle |
1668 | * strip. |
1669 | */ |
1670 | GLushort *stripIdx; |
1671 | /* Create index vector */ |
1672 | GLushort offset; |
1673 | |
1674 | /* Allocate buffers for indices, bail out if memory allocation fails */ |
1675 | stripIdx = malloc((slices+1)*2*(stacks)*sizeof(GLushort)); |
1676 | if (!(stripIdx)) |
1677 | { |
1678 | free(stripIdx); |
1679 | fgError("Failed to allocate memory in fghSphere" ); |
1680 | } |
1681 | |
1682 | /* top stack */ |
1683 | for (j=0, idx=0; j<slices; j++, idx+=2) |
1684 | { |
1685 | stripIdx[idx ] = j+1; /* 0 is top vertex, 1 is first for first stack */ |
1686 | stripIdx[idx+1] = 0; |
1687 | } |
1688 | stripIdx[idx ] = 1; /* repeat first slice's idx for closing off shape */ |
1689 | stripIdx[idx+1] = 0; |
1690 | idx+=2; |
1691 | |
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) |
1695 | { |
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) |
1698 | { |
1699 | stripIdx[idx ] = offset+j+slices; |
1700 | stripIdx[idx+1] = offset+j; |
1701 | } |
1702 | stripIdx[idx ] = offset+slices; /* repeat first slice's idx for closing off shape */ |
1703 | stripIdx[idx+1] = offset; |
1704 | } |
1705 | |
1706 | /* bottom stack */ |
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) |
1709 | { |
1710 | stripIdx[idx ] = nVert-1; /* zero based index, last element in array (bottom vertex)... */ |
1711 | stripIdx[idx+1] = offset+j; |
1712 | } |
1713 | stripIdx[idx ] = nVert-1; /* repeat first slice's idx for closing off shape */ |
1714 | stripIdx[idx+1] = offset; |
1715 | |
1716 | |
1717 | /* draw */ |
1718 | fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,stacks,(slices+1)*2); |
1719 | |
1720 | /* cleanup allocated memory */ |
1721 | free(stripIdx); |
1722 | } |
1723 | |
1724 | /* cleanup allocated memory */ |
1725 | free(vertices); |
1726 | free(normals); |
1727 | } |
1728 | |
1729 | static void fghCone( GLfloat base, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode ) |
1730 | { |
1731 | int i,j,idx, nVert; |
1732 | GLfloat *vertices, *normals; |
1733 | |
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); |
1737 | |
1738 | if (nVert==0) |
1739 | /* nothing to draw */ |
1740 | return; |
1741 | |
1742 | if (useWireMode) |
1743 | { |
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. |
1748 | */ |
1749 | |
1750 | stackIdx = malloc(slices*stacks*sizeof(GLushort)); |
1751 | sliceIdx = malloc(slices*2 *sizeof(GLushort)); |
1752 | if (!(stackIdx) || !(sliceIdx)) |
1753 | { |
1754 | free(stackIdx); |
1755 | free(sliceIdx); |
1756 | fgError("Failed to allocate memory in fghCone" ); |
1757 | } |
1758 | |
1759 | /* generate for each stack */ |
1760 | for (i=0,idx=0; i<stacks; i++) |
1761 | { |
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++) |
1764 | { |
1765 | stackIdx[idx] = offset+j; |
1766 | } |
1767 | } |
1768 | |
1769 | /* generate for each slice */ |
1770 | for (i=0,idx=0; i<slices; i++) |
1771 | { |
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; |
1775 | } |
1776 | |
1777 | /* draw */ |
1778 | fghDrawGeometryWire(vertices,normals,nVert, |
1779 | sliceIdx,1,slices*2,GL_LINES, |
1780 | stackIdx,stacks,slices); |
1781 | |
1782 | /* cleanup allocated memory */ |
1783 | free(sliceIdx); |
1784 | free(stackIdx); |
1785 | } |
1786 | else |
1787 | { |
1788 | /* First, generate vertex index arrays for drawing with glDrawElements |
1789 | * All stacks, including top and bottom are covered with a triangle |
1790 | * strip. |
1791 | */ |
1792 | GLushort *stripIdx; |
1793 | /* Create index vector */ |
1794 | GLushort offset; |
1795 | |
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 */ |
1798 | if (!(stripIdx)) |
1799 | { |
1800 | free(stripIdx); |
1801 | fgError("Failed to allocate memory in fghCone" ); |
1802 | } |
1803 | |
1804 | /* top stack */ |
1805 | for (j=0, idx=0; j<slices; j++, idx+=2) |
1806 | { |
1807 | stripIdx[idx ] = 0; |
1808 | stripIdx[idx+1] = j+1; /* 0 is top vertex, 1 is first for first stack */ |
1809 | } |
1810 | stripIdx[idx ] = 0; /* repeat first slice's idx for closing off shape */ |
1811 | stripIdx[idx+1] = 1; |
1812 | idx+=2; |
1813 | |
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) |
1817 | { |
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) |
1820 | { |
1821 | stripIdx[idx ] = offset+j; |
1822 | stripIdx[idx+1] = offset+j+slices; |
1823 | } |
1824 | stripIdx[idx ] = offset; /* repeat first slice's idx for closing off shape */ |
1825 | stripIdx[idx+1] = offset+slices; |
1826 | } |
1827 | |
1828 | /* draw */ |
1829 | fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,stacks+1,(slices+1)*2); |
1830 | |
1831 | /* cleanup allocated memory */ |
1832 | free(stripIdx); |
1833 | } |
1834 | |
1835 | /* cleanup allocated memory */ |
1836 | free(vertices); |
1837 | free(normals); |
1838 | } |
1839 | |
1840 | static void fghCylinder( GLfloat radius, GLfloat height, GLint slices, GLint stacks, GLboolean useWireMode ) |
1841 | { |
1842 | int i,j,idx, nVert; |
1843 | GLfloat *vertices, *normals; |
1844 | |
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); |
1848 | |
1849 | if (nVert==0) |
1850 | /* nothing to draw */ |
1851 | return; |
1852 | |
1853 | if (useWireMode) |
1854 | { |
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. |
1859 | */ |
1860 | |
1861 | stackIdx = malloc(slices*(stacks+1)*sizeof(GLushort)); |
1862 | sliceIdx = malloc(slices*2 *sizeof(GLushort)); |
1863 | if (!(stackIdx) || !(sliceIdx)) |
1864 | { |
1865 | free(stackIdx); |
1866 | free(sliceIdx); |
1867 | fgError("Failed to allocate memory in fghCylinder" ); |
1868 | } |
1869 | |
1870 | /* generate for each stack */ |
1871 | for (i=0,idx=0; i<stacks+1; i++) |
1872 | { |
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++) |
1875 | { |
1876 | stackIdx[idx] = offset+j; |
1877 | } |
1878 | } |
1879 | |
1880 | /* generate for each slice */ |
1881 | for (i=0,idx=0; i<slices; i++) |
1882 | { |
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; |
1886 | } |
1887 | |
1888 | /* draw */ |
1889 | fghDrawGeometryWire(vertices,normals,nVert, |
1890 | sliceIdx,1,slices*2,GL_LINES, |
1891 | stackIdx,stacks+1,slices); |
1892 | |
1893 | /* cleanup allocated memory */ |
1894 | free(sliceIdx); |
1895 | free(stackIdx); |
1896 | } |
1897 | else |
1898 | { |
1899 | /* First, generate vertex index arrays for drawing with glDrawElements |
1900 | * All stacks, including top and bottom are covered with a triangle |
1901 | * strip. |
1902 | */ |
1903 | GLushort *stripIdx; |
1904 | /* Create index vector */ |
1905 | GLushort offset; |
1906 | |
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 */ |
1909 | if (!(stripIdx)) |
1910 | { |
1911 | free(stripIdx); |
1912 | fgError("Failed to allocate memory in fghCylinder" ); |
1913 | } |
1914 | |
1915 | /* top stack */ |
1916 | for (j=0, idx=0; j<slices; j++, idx+=2) |
1917 | { |
1918 | stripIdx[idx ] = 0; |
1919 | stripIdx[idx+1] = j+1; /* 0 is top vertex, 1 is first for first stack */ |
1920 | } |
1921 | stripIdx[idx ] = 0; /* repeat first slice's idx for closing off shape */ |
1922 | stripIdx[idx+1] = 1; |
1923 | idx+=2; |
1924 | |
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) |
1928 | { |
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) |
1931 | { |
1932 | stripIdx[idx ] = offset+j; |
1933 | stripIdx[idx+1] = offset+j+slices; |
1934 | } |
1935 | stripIdx[idx ] = offset; /* repeat first slice's idx for closing off shape */ |
1936 | stripIdx[idx+1] = offset+slices; |
1937 | } |
1938 | |
1939 | /* top stack */ |
1940 | offset = 1+(stacks+2)*slices; |
1941 | for (j=0; j<slices; j++, idx+=2) |
1942 | { |
1943 | stripIdx[idx ] = offset+j; |
1944 | stripIdx[idx+1] = nVert-1; /* zero based index, last element in array (bottom vertex)... */ |
1945 | } |
1946 | stripIdx[idx ] = offset; |
1947 | stripIdx[idx+1] = nVert-1; /* repeat first slice's idx for closing off shape */ |
1948 | |
1949 | /* draw */ |
1950 | fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,stacks+2,(slices+1)*2); |
1951 | |
1952 | /* cleanup allocated memory */ |
1953 | free(stripIdx); |
1954 | } |
1955 | |
1956 | /* cleanup allocated memory */ |
1957 | free(vertices); |
1958 | free(normals); |
1959 | } |
1960 | |
1961 | static void fghTorus( GLfloat dInnerRadius, GLfloat dOuterRadius, GLint nSides, GLint nRings, GLboolean useWireMode ) |
1962 | { |
1963 | int i,j,idx, nVert; |
1964 | GLfloat *vertices, *normals; |
1965 | |
1966 | /* Generate vertices and normals */ |
1967 | fghGenerateTorus(dInnerRadius,dOuterRadius,nSides,nRings, &vertices,&normals,&nVert); |
1968 | |
1969 | if (nVert==0) |
1970 | /* nothing to draw */ |
1971 | return; |
1972 | |
1973 | if (useWireMode) |
1974 | { |
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. |
1979 | */ |
1980 | |
1981 | ringIdx = malloc(nRings*nSides*sizeof(GLushort)); |
1982 | sideIdx = malloc(nSides*nRings*sizeof(GLushort)); |
1983 | if (!(ringIdx) || !(sideIdx)) |
1984 | { |
1985 | free(ringIdx); |
1986 | free(sideIdx); |
1987 | fgError("Failed to allocate memory in fghTorus" ); |
1988 | } |
1989 | |
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; |
1994 | |
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; |
1999 | |
2000 | /* draw */ |
2001 | fghDrawGeometryWire(vertices,normals,nVert, |
2002 | ringIdx,nRings,nSides,GL_LINE_LOOP, |
2003 | sideIdx,nSides,nRings); |
2004 | |
2005 | /* cleanup allocated memory */ |
2006 | free(sideIdx); |
2007 | free(ringIdx); |
2008 | } |
2009 | else |
2010 | { |
2011 | /* First, generate vertex index arrays for drawing with glDrawElements |
2012 | * All stacks, including top and bottom are covered with a triangle |
2013 | * strip. |
2014 | */ |
2015 | GLushort *stripIdx; |
2016 | |
2017 | /* Allocate buffers for indices, bail out if memory allocation fails */ |
2018 | stripIdx = malloc((nRings+1)*2*nSides*sizeof(GLushort)); |
2019 | if (!(stripIdx)) |
2020 | { |
2021 | free(stripIdx); |
2022 | fgError("Failed to allocate memory in fghTorus" ); |
2023 | } |
2024 | |
2025 | for( i=0, idx=0; i<nSides; i++ ) |
2026 | { |
2027 | int ioff = 1; |
2028 | if (i==nSides-1) |
2029 | ioff = -i; |
2030 | |
2031 | for( j=0; j<nRings; j++, idx+=2 ) |
2032 | { |
2033 | int offset = j * nSides + i; |
2034 | stripIdx[idx ] = offset; |
2035 | stripIdx[idx+1] = offset + ioff; |
2036 | } |
2037 | /* repeat first to close off shape */ |
2038 | stripIdx[idx ] = i; |
2039 | stripIdx[idx+1] = i + ioff; |
2040 | idx +=2; |
2041 | } |
2042 | |
2043 | /* draw */ |
2044 | fghDrawGeometrySolid(vertices,normals,NULL,nVert,stripIdx,nSides,(nRings+1)*2); |
2045 | |
2046 | /* cleanup allocated memory */ |
2047 | free(stripIdx); |
2048 | } |
2049 | |
2050 | /* cleanup allocated memory */ |
2051 | free(vertices); |
2052 | free(normals); |
2053 | } |
2054 | |
2055 | |
2056 | /* -- INTERFACE FUNCTIONS ---------------------------------------------- */ |
2057 | |
2058 | |
2059 | /* |
2060 | * Draws a solid sphere |
2061 | */ |
2062 | void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks) |
2063 | { |
2064 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSphere" ); |
2065 | fghSphere((GLfloat)radius, slices, stacks, GL_FALSE ); |
2066 | } |
2067 | |
2068 | /* |
2069 | * Draws a wire sphere |
2070 | */ |
2071 | void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks) |
2072 | { |
2073 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSphere" ); |
2074 | fghSphere((GLfloat)radius, slices, stacks, GL_TRUE ); |
2075 | |
2076 | } |
2077 | |
2078 | /* |
2079 | * Draws a solid cone |
2080 | */ |
2081 | void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks ) |
2082 | { |
2083 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCone" ); |
2084 | fghCone((GLfloat)base, (GLfloat)height, slices, stacks, GL_FALSE ); |
2085 | } |
2086 | |
2087 | /* |
2088 | * Draws a wire cone |
2089 | */ |
2090 | void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks) |
2091 | { |
2092 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCone" ); |
2093 | fghCone((GLfloat)base, (GLfloat)height, slices, stacks, GL_TRUE ); |
2094 | } |
2095 | |
2096 | |
2097 | /* |
2098 | * Draws a solid cylinder |
2099 | */ |
2100 | void FGAPIENTRY glutSolidCylinder(double radius, double height, GLint slices, GLint stacks) |
2101 | { |
2102 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCylinder" ); |
2103 | fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, GL_FALSE ); |
2104 | } |
2105 | |
2106 | /* |
2107 | * Draws a wire cylinder |
2108 | */ |
2109 | void FGAPIENTRY glutWireCylinder(double radius, double height, GLint slices, GLint stacks) |
2110 | { |
2111 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCylinder" ); |
2112 | fghCylinder((GLfloat)radius, (GLfloat)height, slices, stacks, GL_TRUE ); |
2113 | } |
2114 | |
2115 | /* |
2116 | * Draws a wire torus |
2117 | */ |
2118 | void FGAPIENTRY glutWireTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings ) |
2119 | { |
2120 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireTorus" ); |
2121 | fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, GL_TRUE); |
2122 | } |
2123 | |
2124 | /* |
2125 | * Draws a solid torus |
2126 | */ |
2127 | void FGAPIENTRY glutSolidTorus( double dInnerRadius, double dOuterRadius, GLint nSides, GLint nRings ) |
2128 | { |
2129 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidTorus" ); |
2130 | fghTorus((GLfloat)dInnerRadius, (GLfloat)dOuterRadius, nSides, nRings, GL_FALSE); |
2131 | } |
2132 | |
2133 | |
2134 | |
2135 | /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ |
2136 | /* Macro to generate interface functions */ |
2137 | #define DECLARE_SHAPE_INTERFACE(nameICaps)\ |
2138 | void FGAPIENTRY glutWire##nameICaps( void )\ |
2139 | {\ |
2140 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWire"#nameICaps );\ |
2141 | fgh##nameICaps( GL_TRUE );\ |
2142 | }\ |
2143 | void FGAPIENTRY glutSolid##nameICaps( void )\ |
2144 | {\ |
2145 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolid"#nameICaps );\ |
2146 | fgh##nameICaps( GL_FALSE );\ |
2147 | } |
2148 | |
2149 | void FGAPIENTRY glutWireCube( double dSize ) |
2150 | { |
2151 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireCube" ); |
2152 | fghCube( (GLfloat)dSize, GL_TRUE ); |
2153 | } |
2154 | void FGAPIENTRY glutSolidCube( double dSize ) |
2155 | { |
2156 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidCube" ); |
2157 | fghCube( (GLfloat)dSize, GL_FALSE ); |
2158 | } |
2159 | |
2160 | DECLARE_SHAPE_INTERFACE(Dodecahedron) |
2161 | DECLARE_SHAPE_INTERFACE(Icosahedron) |
2162 | DECLARE_SHAPE_INTERFACE(Octahedron) |
2163 | DECLARE_SHAPE_INTERFACE(RhombicDodecahedron) |
2164 | |
2165 | void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale ) |
2166 | { |
2167 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWireSierpinskiSponge" ); |
2168 | fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, GL_TRUE ); |
2169 | } |
2170 | void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale ) |
2171 | { |
2172 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSolidSierpinskiSponge" ); |
2173 | fghSierpinskiSponge ( num_levels, offset, (GLfloat)scale, GL_FALSE ); |
2174 | } |
2175 | |
2176 | DECLARE_SHAPE_INTERFACE(Tetrahedron) |
2177 | |
2178 | |
2179 | /*** END OF FILE ***/ |
2180 | |