/* * fg_geometry.c * * Freeglut geometry rendering methods. * * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. * Written by Pawel W. Olszta, * Creation date: Fri Dec 3 1999 * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "fg_internal.h" #include "fg_gl2.h" #include /* * A note: We do not use the GLuint data type for vertex index arrays * in this code as Open GL ES1 only supports GLushort. This affects the * cylindrical objects only (Torus, Sphere, Cylinder and Cone) and limits * their number of vertices to 65535 (2^16-1). Thats about 256*256 * subdivisions, which is sufficient for just about any usage case, so * I am not going to worry about it for now. * One could do compile time detection of the gluint type through CMake, * but it is likely that we'll eventually move to runtime selection * of OpenGL or GLES1/2, which would make that strategy useless... */ /* declare for drawing using the different OpenGL versions here so we can have a nice code order below */ static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2 ); static void fghDrawGeometrySolid11(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart); static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2, GLint attribute_v_coord, GLint attribute_v_normal ); static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart, GLint attribute_v_coord, GLint attribute_v_normal, GLint attribute_v_texture); /* declare function for generating visualization of normals */ static void fghGenerateNormalVisualization(GLfloat *vertices, GLfloat *normals, GLsizei numVertices); static void fghDrawNormalVisualization11(); static void fghDrawNormalVisualization20(GLint attribute_v_coord); /* Drawing geometry: * Explanation of the functions has to be separate for the polyhedra and * the non-polyhedra (objects with a circular cross-section). * Polyhedra: * - We have only implemented the five platonic solids and the rhomboid * dodecahedron. If you need more types of polyhedra, please see * CPolyhedron in MRPT * - Solids are drawn by glDrawArrays if composed of triangular faces * (the tetrahedron, octahedron, and icosahedron), or are first * decomposed into triangles and then drawn by glDrawElements if its * faces are squares or pentagons (cube, dodecahedron and rhombic * dodecahedron) as some vertices are repeated in that case. * - WireFrame drawing is done using a GL_LINE_LOOP per face, and thus * issuing one draw call per face. glDrawArrays is always used as no * triangle decomposition is needed to draw faces. We use the "first" * parameter in glDrawArrays to go from face to face. * * Non-polyhedra: * - We have implemented the sphere, cylinder, cone and torus. * - All shapes are characterized by two parameters: the number of * subdivisions along two axes used to construct the shape's vertices * (e.g. stacks and slices for the sphere). * As different subdivisions are most suitable for different shapes, * and are thus also named differently, I wont provide general comments * on them here. * - Solids are drawn using glDrawArrays and GL_TRIANGLE_STRIP. Each * strip covers one revolution around one of the two subdivision axes * of the shape. * - WireFrame drawing is done for the subdivisions along the two axes * separately, usually using GL_LINE_LOOP. Vertex index arrays are * built containing the vertices to be drawn for each loop, which are * then drawn using multiple calls to glDrawElements. As the number of * subdivisions along the two axes is not guaranteed to be equal, the * vertex indices for e.g. stacks and slices are stored in separate * arrays, which makes the input to the drawing function a bit clunky, * but allows for the same drawing function to be used for all shapes. */ /** * Draw geometric shape in wire mode (only edges) * * Arguments: * GLfloat *vertices, GLfloat *normals, GLsizei numVertices * The vertex coordinate and normal buffers, and the number of entries in * those * GLushort *vertIdxs * a vertex indices buffer, optional (never passed for the polyhedra) * GLsizei numParts, GLsizei numVertPerPart * polyhedra: number of faces, and the number of vertices for drawing * each face * non-polyhedra: number of edges to draw for first subdivision (not * necessarily equal to number of subdivisions requested by user, e.g. * as each subdivision is enclosed by two edges), and number of * vertices for drawing each * numParts * numVertPerPart gives the number of entries in the vertex * array vertIdxs * GLenum vertexMode * vertex drawing mode (e.g. always GL_LINE_LOOP for polyhedra, varies * for others) * GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2 * non-polyhedra only: same as the above, but now for subdivisions along * the other axis. Always drawn as GL_LINE_LOOP. * * Feel free to contribute better naming ;) */ void fghDrawGeometryWire(GLfloat *vertices, GLfloat *normals, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2 ) { GLint attribute_v_coord = fgStructure.CurrentWindow->Window.attribute_v_coord; GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal; if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1)) /* User requested a 2.0 draw */ fghDrawGeometryWire20(vertices, normals, numVertices, vertIdxs, numParts, numVertPerPart, vertexMode, vertIdxs2, numParts2, numVertPerPart2, attribute_v_coord, attribute_v_normal); else fghDrawGeometryWire11(vertices, normals, vertIdxs, numParts, numVertPerPart, vertexMode, vertIdxs2, numParts2, numVertPerPart2); } /* Draw the geometric shape with filled triangles * * Arguments: * GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices * The vertex coordinate, normal and texture coordinate buffers, and the * number of entries in those * GLushort *vertIdxs * a vertex indices buffer, optional (not passed for the polyhedra with * triangular faces) * GLsizei numParts, GLsizei numVertPerPart * polyhedra: not used for polyhedra with triangular faces (numEdgePerFace==3), as each vertex+normal pair is drawn only once, so no vertex indices are used. Else, the shape was triangulated (DECOMPOSE_TO_TRIANGLE), leading to reuse of some vertex+normal pairs, and thus the need to draw with glDrawElements. numParts is always 1 in this case (we can draw the whole object with one call to glDrawElements as the vertex index array contains separate triangles), and numVertPerPart indicates the number of vertex indices in the vertex array. * non-polyhedra: number of parts (GL_TRIANGLE_STRIPs) to be drawn separately (numParts calls to glDrawElements) to create the object. numVertPerPart indicates the number of vertex indices to be processed at each draw call. * numParts * numVertPerPart gives the number of entries in the vertex * array vertIdxs */ void fghDrawGeometrySolid(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart) { GLint attribute_v_coord = fgStructure.CurrentWindow->Window.attribute_v_coord; GLint attribute_v_normal = fgStructure.CurrentWindow->Window.attribute_v_normal; GLint attribute_v_texture = fgStructure.CurrentWindow->Window.attribute_v_texture; if (fgStructure.CurrentWindow->State.VisualizeNormals) /* generate normals for each vertex to be drawn as well */ fghGenerateNormalVisualization(vertices, normals, numVertices); if (fgState.HasOpenGL20 && (attribute_v_coord != -1 || attribute_v_normal != -1)) { /* User requested a 2.0 draw */ fghDrawGeometrySolid20(vertices, normals, textcs, numVertices, vertIdxs, numParts, numVertIdxsPerPart, attribute_v_coord, attribute_v_normal, attribute_v_texture); if (fgStructure.CurrentWindow->State.VisualizeNormals) /* draw normals for each vertex as well */ fghDrawNormalVisualization20(attribute_v_coord); } else { fghDrawGeometrySolid11(vertices, normals, textcs, numVertices, vertIdxs, numParts, numVertIdxsPerPart); if (fgStructure.CurrentWindow->State.VisualizeNormals) /* draw normals for each vertex as well */ fghDrawNormalVisualization11(); } } /* Version for OpenGL (ES) 1.1 */ static void fghDrawGeometryWire11(GLfloat *vertices, GLfloat *normals, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2 ) { int i; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertices); glNormalPointer(GL_FLOAT, 0, normals); if (!vertIdxs) /* Draw per face (TODO: could use glMultiDrawArrays if available) */ for (i=0; i1) for (i=0; i= 2.0 */ static void fghDrawGeometryWire20(GLfloat *vertices, GLfloat *normals, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertPerPart, GLenum vertexMode, GLushort *vertIdxs2, GLsizei numParts2, GLsizei numVertPerPart2, GLint attribute_v_coord, GLint attribute_v_normal) { GLuint vbo_coords = 0, vbo_normals = 0, ibo_elements = 0, ibo_elements2 = 0; GLsizei numVertIdxs = numParts * numVertPerPart; GLsizei numVertIdxs2 = numParts2 * numVertPerPart2; int i; if (numVertices > 0 && attribute_v_coord != -1) { fghGenBuffers(1, &vbo_coords); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]), vertices, FGH_STATIC_DRAW); } if (numVertices > 0 && attribute_v_normal != -1) { fghGenBuffers(1, &vbo_normals); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]), normals, FGH_STATIC_DRAW); } if (vertIdxs != NULL) { fghGenBuffers(1, &ibo_elements); fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements); fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]), vertIdxs, FGH_STATIC_DRAW); fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); } if (vertIdxs2 != NULL) { fghGenBuffers(1, &ibo_elements2); fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements2); fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs2 * sizeof(vertIdxs2[0]), vertIdxs2, FGH_STATIC_DRAW); fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); } if (vbo_coords) { fghEnableVertexAttribArray(attribute_v_coord); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); fghVertexAttribPointer( attribute_v_coord, /* attribute */ 3, /* number of elements per vertex, here (x,y,z) */ GL_FLOAT, /* the type of each element */ GL_FALSE, /* take our values as-is */ 0, /* no extra data between each position */ 0 /* offset of first element */ ); fghBindBuffer(FGH_ARRAY_BUFFER, 0); } if (vbo_normals) { fghEnableVertexAttribArray(attribute_v_normal); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); fghVertexAttribPointer( attribute_v_normal, /* attribute */ 3, /* number of elements per vertex, here (x,y,z) */ GL_FLOAT, /* the type of each element */ GL_FALSE, /* take our values as-is */ 0, /* no extra data between each position */ 0 /* offset of first element */ ); fghBindBuffer(FGH_ARRAY_BUFFER, 0); } if (!vertIdxs) { /* Draw per face (TODO: could use glMultiDrawArrays if available) */ for (i=0; i= 2.0 */ static void fghDrawGeometrySolid20(GLfloat *vertices, GLfloat *normals, GLfloat *textcs, GLsizei numVertices, GLushort *vertIdxs, GLsizei numParts, GLsizei numVertIdxsPerPart, GLint attribute_v_coord, GLint attribute_v_normal, GLint attribute_v_texture) { GLuint vbo_coords = 0, vbo_normals = 0, vbo_textcs = 0, ibo_elements = 0; GLsizei numVertIdxs = numParts * numVertIdxsPerPart; int i; if (numVertices > 0 && attribute_v_coord != -1) { fghGenBuffers(1, &vbo_coords); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(vertices[0]), vertices, FGH_STATIC_DRAW); fghBindBuffer(FGH_ARRAY_BUFFER, 0); } if (numVertices > 0 && attribute_v_normal != -1) { fghGenBuffers(1, &vbo_normals); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); fghBufferData(FGH_ARRAY_BUFFER, numVertices * 3 * sizeof(normals[0]), normals, FGH_STATIC_DRAW); fghBindBuffer(FGH_ARRAY_BUFFER, 0); } if (numVertices > 0 && attribute_v_texture != -1 && textcs) { fghGenBuffers(1, &vbo_textcs); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs); fghBufferData(FGH_ARRAY_BUFFER, numVertices * 2 * sizeof(textcs[0]), textcs, FGH_STATIC_DRAW); fghBindBuffer(FGH_ARRAY_BUFFER, 0); } if (vertIdxs != NULL) { fghGenBuffers(1, &ibo_elements); fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements); fghBufferData(FGH_ELEMENT_ARRAY_BUFFER, numVertIdxs * sizeof(vertIdxs[0]), vertIdxs, FGH_STATIC_DRAW); fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, 0); } if (vbo_coords) { fghEnableVertexAttribArray(attribute_v_coord); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); fghVertexAttribPointer( attribute_v_coord, /* attribute */ 3, /* number of elements per vertex, here (x,y,z) */ GL_FLOAT, /* the type of each element */ GL_FALSE, /* take our values as-is */ 0, /* no extra data between each position */ 0 /* offset of first element */ ); fghBindBuffer(FGH_ARRAY_BUFFER, 0); }; if (vbo_normals) { fghEnableVertexAttribArray(attribute_v_normal); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_normals); fghVertexAttribPointer( attribute_v_normal, /* attribute */ 3, /* number of elements per vertex, here (x,y,z) */ GL_FLOAT, /* the type of each element */ GL_FALSE, /* take our values as-is */ 0, /* no extra data between each position */ 0 /* offset of first element */ ); fghBindBuffer(FGH_ARRAY_BUFFER, 0); }; if (vbo_textcs) { fghEnableVertexAttribArray(attribute_v_texture); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_textcs); fghVertexAttribPointer( attribute_v_texture,/* attribute */ 2, /* number of elements per vertex, here (s,t) */ GL_FLOAT, /* the type of each element */ GL_FALSE, /* take our values as-is */ 0, /* no extra data between each position */ 0 /* offset of first element */ ); fghBindBuffer(FGH_ARRAY_BUFFER, 0); }; if (vertIdxs == NULL) { glDrawArrays(GL_TRIANGLES, 0, numVertices); } else { fghBindBuffer(FGH_ELEMENT_ARRAY_BUFFER, ibo_elements); if (numParts>1) { for (i=0; i= 2.0 */ static void fghDrawNormalVisualization20(GLint attribute_v_coord) { GLuint vbo_coords = 0; if (attribute_v_coord != -1) { fghGenBuffers(1, &vbo_coords); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); fghBufferData(FGH_ARRAY_BUFFER, numNormalVertices * 3 * sizeof(verticesForNormalVisualization[0]), verticesForNormalVisualization, FGH_STATIC_DRAW); } if (vbo_coords) { fghEnableVertexAttribArray(attribute_v_coord); fghBindBuffer(FGH_ARRAY_BUFFER, vbo_coords); fghVertexAttribPointer( attribute_v_coord, /* attribute */ 3, /* number of elements per vertex, here (x,y,z) */ GL_FLOAT, /* the type of each element */ GL_FALSE, /* take our values as-is */ 0, /* no extra data between each position */ 0 /* offset of first element */ ); fghBindBuffer(FGH_ARRAY_BUFFER, 0); } glDrawArrays(GL_LINES, 0, numNormalVertices); if (vbo_coords != 0) fghDisableVertexAttribArray(attribute_v_coord); if (vbo_coords != 0) fghDeleteBuffers(1, &vbo_coords); /* Done, free memory */ free(verticesForNormalVisualization); } /** * Generate all combinations of vertices and normals needed to draw object. * Optional shape decomposition to triangles: * We'll use glDrawElements to draw all shapes that are not naturally * composed of triangles, so generate an index vector here, using the * below sampling scheme. * Be careful to keep winding of all triangles counter-clockwise, * assuming that input has correct winding... */ static GLubyte vert4Decomp[6] = {0,1,2, 0,2,3}; /* quad : 4 input vertices, 6 output (2 triangles) */ static GLubyte vert5Decomp[9] = {0,1,2, 0,2,4, 4,2,3}; /* pentagon: 5 input vertices, 9 output (3 triangles) */ static void fghGenerateGeometryWithIndexArray(int numFaces, int numEdgePerFace, GLfloat *vertices, GLubyte *vertIndices, GLfloat *normals, GLfloat *vertOut, GLfloat *normOut, GLushort *vertIdxOut) { int i,j,numEdgeIdxPerFace; GLubyte *vertSamps = NULL; switch (numEdgePerFace) { case 3: /* nothing to do here, we'll draw with glDrawArrays */ break; case 4: vertSamps = vert4Decomp; numEdgeIdxPerFace = 6; /* 6 output vertices for each face */ break; case 5: vertSamps = vert5Decomp; numEdgeIdxPerFace = 9; /* 9 output vertices for each face */ break; } /* * Build array with vertices using vertex coordinates and vertex indices * Do same for normals. * Need to do this because of different normals at shared vertices. */ for (i=0; i 0 ) { double local_offset[3] ; /* Use a local variable to avoid buildup of roundoff errors */ unsigned int stride = ipow(4,--numLevels)*TETRAHEDRON_VERT_ELEM_PER_OBJ; scale /= 2.0 ; for ( i = 0 ; i < TETRAHEDRON_NUM_FACES ; i++ ) { int idx = i*3; local_offset[0] = offset[0] + scale * tetrahedron_v[idx ]; local_offset[1] = offset[1] + scale * tetrahedron_v[idx+1]; local_offset[2] = offset[2] + scale * tetrahedron_v[idx+2]; fghSierpinskiSpongeGenerate ( numLevels, local_offset, scale, vertices+i*stride, normals+i*stride ); } } } /* -- Now the various non-polyhedra (shapes involving circles) -- */ /* * Compute lookup table of cos and sin values forming a circle * (or half circle if halfCircle==TRUE) * * Notes: * It is the responsibility of the caller to free these tables * The size of the table is (n+1) to form a connected loop * The last entry is exactly the same as the first * The sign of n can be flipped to get the reverse loop */ static void fghCircleTable(GLfloat **sint, GLfloat **cost, const int n, const GLboolean halfCircle) { int i; /* Table size, the sign of n flips the circle direction */ const int size = abs(n); /* Determine the angle between samples */ const GLfloat angle = (halfCircle?1:2)*(GLfloat)M_PI/(GLfloat)( ( n == 0 ) ? 1 : n ); /* Allocate memory for n samples, plus duplicate of first entry at the end */ *sint = malloc(sizeof(GLfloat) * (size+1)); *cost = malloc(sizeof(GLfloat) * (size+1)); /* Bail out if memory allocation fails, fgError never returns */ if (!(*sint) || !(*cost)) { free(*sint); free(*cost); fgError("Failed to allocate memory in fghCircleTable"); } /* Compute cos and sin around the circle */ (*sint)[0] = 0.0; (*cost)[0] = 1.0; for (i=1; i 65535) /* * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above */ fgWarning("fghGenerateSphere: too many slices or stacks requested, indices will wrap"); /* precompute values on unit circle */ fghCircleTable(&sint1,&cost1,-slices,GL_FALSE); fghCircleTable(&sint2,&cost2, stacks,GL_TRUE); /* Allocate vertex and normal buffers, bail out if memory allocation fails */ *vertices = malloc((*nVert)*3*sizeof(GLfloat)); *normals = malloc((*nVert)*3*sizeof(GLfloat)); if (!(*vertices) || !(*normals)) { free(*vertices); free(*normals); fgError("Failed to allocate memory in fghGenerateSphere"); } /* top */ (*vertices)[0] = 0.f; (*vertices)[1] = 0.f; (*vertices)[2] = radius; (*normals )[0] = 0.f; (*normals )[1] = 0.f; (*normals )[2] = 1.f; idx = 3; /* each stack */ for( i=1; i 0 ) ? stacks : 1 ); const GLfloat rStep = (GLfloat)base / ( ( stacks > 0 ) ? stacks : 1 ); /* Scaling factors for vertex normals */ const GLfloat cosn = (GLfloat) (height / sqrt( height * height + base * base )); const GLfloat sinn = (GLfloat) (base / sqrt( height * height + base * base )); /* number of unique vertices */ if (slices==0 || stacks<1) { /* nothing to generate */ *nVert = 0; return; } *nVert = slices*(stacks+2)+1; /* need an extra stack for closing off bottom with correct normals */ if ((*nVert) > 65535) /* * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above */ fgWarning("fghGenerateCone: too many slices or stacks requested, indices will wrap"); /* Pre-computed circle */ fghCircleTable(&sint,&cost,-slices,GL_FALSE); /* Allocate vertex and normal buffers, bail out if memory allocation fails */ *vertices = malloc((*nVert)*3*sizeof(GLfloat)); *normals = malloc((*nVert)*3*sizeof(GLfloat)); if (!(*vertices) || !(*normals)) { free(*vertices); free(*normals); fgError("Failed to allocate memory in fghGenerateCone"); } /* bottom */ (*vertices)[0] = 0.f; (*vertices)[1] = 0.f; (*vertices)[2] = z; (*normals )[0] = 0.f; (*normals )[1] = 0.f; (*normals )[2] = -1.f; idx = 3; /* other on bottom (get normals right) */ for (j=0; j 0 ) ? stacks : 1 ); /* Pre-computed circle */ GLfloat *sint,*cost; /* number of unique vertices */ if (slices==0 || stacks<1) { /* nothing to generate */ *nVert = 0; return; } *nVert = slices*(stacks+3)+2; /* need two extra stacks for closing off top and bottom with correct normals */ if ((*nVert) > 65535) /* * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above */ fgWarning("fghGenerateCylinder: too many slices or stacks requested, indices will wrap"); /* Pre-computed circle */ fghCircleTable(&sint,&cost,-slices,GL_FALSE); /* Allocate vertex and normal buffers, bail out if memory allocation fails */ *vertices = malloc((*nVert)*3*sizeof(GLfloat)); *normals = malloc((*nVert)*3*sizeof(GLfloat)); if (!(*vertices) || !(*normals)) { free(*vertices); free(*normals); fgError("Failed to allocate memory in fghGenerateCylinder"); } z=0; /* top on Z-axis */ (*vertices)[0] = 0.f; (*vertices)[1] = 0.f; (*vertices)[2] = 0.f; (*normals )[0] = 0.f; (*normals )[1] = 0.f; (*normals )[2] = -1.f; idx = 3; /* other on top (get normals right) */ for (j=0; j 65535) /* * limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above */ fgWarning("fghGenerateTorus: too many slices or stacks requested, indices will wrap"); /* precompute values on unit circle */ fghCircleTable(&spsi,&cpsi, nRings,GL_FALSE); fghCircleTable(&sphi,&cphi,-nSides,GL_FALSE); /* Allocate vertex and normal buffers, bail out if memory allocation fails */ *vertices = malloc((*nVert)*3*sizeof(GLfloat)); *normals = malloc((*nVert)*3*sizeof(GLfloat)); if (!(*vertices) || !(*normals)) { free(*vertices); free(*normals); fgError("Failed to allocate memory in fghGenerateTorus"); } for( j=0; j