//////////////////////////////////////////////////////////////////////////// // // OpenGL GLU tesselation (simple). // - Originaly written by Valery Moya (vturnip@yahoo.com) // // The big picture is to: // // - Register some callbacks with GLU so we can grab the tesselation // as GLU does it. // // - Give GLU a list of verts that defines the perimeter (of the outside) // of the polygon ordered clockwise, and if any, lists of verts that // make holes in the polygon in counter clockwise order. // // GLU will return triangles, triangle fans and/or triangle strips that // are the triangulation of the polygon. This is done by GLU by first // calling by TessBeginCallback() to let us know the triangle (vertex) // arangement, followed by multiple calls to TessVertexCallback() that // should be interepreted as the verticies of a triangle list, triangle // fan or triangle strip based on what the preceding TessBeginCallback() // set. // // For simplicity here, I assume that the polygon is flat and in the XY // plane (aka, +Z is the polygon's surface normal). // //////////////////////////////////////////////////////////////////////////// #include GLenum __tess_type; // Current type of tesselation: Strip, Fan, List. int __tess_idx[2]; // Keeps track of last two verts of the current triangle. int __tess_nidx; // Number of indecies defined. bool __tess_odd; // For triangle strips, keep track of (flipping) winding order. int __tess_num_trigs; // Total number of triangles generated (tesselated). const GLubyte *__tess_err; // An error occured. /// These next variables are assumed to be defined by you based on your needs: typedef struct VertexRec { double dpos[3]; } Vertex; typedef struct TriangleRec { int vidx[3]; // Indices into the vertex array for the triangle's verts. } Triangle; Vertex *__verts = NULL; // All the verts that define the polygon. // here I assume that the verts are ordered like so: // Verts 0 to (N-1) : defines the (clockwise) perimeter of the poly (has N verts). // Verts N to (N+M-1) : defines the (counter clockwise) perimiter of the first hole in the poly (has M verts). // Verts N+M to (N+M+P-1) : defines the (counter clockwise) perimiter of the second hole in the poly (has P verts). // ... etc for more holes. Triangle *__trigs = NULL; // Will be filled out by the tesselation code. int __contour_vert_count[100]; // Number of verts per contour, so we have: // __contour_vert_count[0] = number of verts for perimeter in poly // __contour_vert_count[1] = number of verts for first hole in poly // __contour_vert_count[2] = number of verts for second hole in poly // ... etc, assumes 100 max contours (or 99 max holes.) int __num_contours = 0; // Total number of contours in poly, should be = 1 + # holes. /// ////////////////////////////////////////////////////////////////// // // TessErrorCallback() // // - Error callback handler if some problem occured while // tesselating. // ////// void APIENTRY TessErrorCallback(GLenum errorCode) { __tess_err = gluErrorString(errorCode); } // TessErrorCallback() /////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // // TessBeginCallback() // // - GLU calls this to let us know about the next list of // triangles (given as triangles, a triangle fan or a triangle // strip) in the triangluation. // ////// void APIENTRY TessBeginCallback(GLenum which) { __tess_type = which; // How next verts should be interpreted to form triangles. __tess_nidx = 0; // __tess_odd = false; // Reset odd-even winding. } // TessBeginCallback() /////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // // TessAddTrig() // // - Keep track of a new triangle defined by vertex indecies a, // b, and c. // ////// void TessAddTrig(int a, int b, int c) { // Add triangle defined by a,b and c to list... if ( __tess_num_trigs >= __num_trigs ) { fprintf(stderr, "Tried to trianglulate too many trigs!\n"); } Triangle *t = __trigs + __tess_num_trigs; t->vidx[0] = a; t->vidx[1] = b; t->vidx[2] = c; __tess_num_trigs++; } // TessAddTrig() ///////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // // TessParseTriangleXXX() // // - Interpret last three vertex indices and keep track of the // triangle defined. // ////// void TessParseTriangle(int idx) { TessAddTrig(__tess_idx[0], __tess_idx[1], idx); // Three unique verts make 1 triangle. __tess_nidx = 0; } void TessParseTriangleFan(int idx) { TessAddTrig(__tess_idx[0], __tess_idx[1], idx); // First vert is anchor of fan, latest index // should make next triangle's first edge. __tess_idx[1] = idx; } void TessParseTriangleStrip(int idx) { if ( !__tess_odd ) { TessAddTrig(__tess_idx[0], __tess_idx[1], idx); } else { TessAddTrig(__tess_idx[0], idx, __tess_idx[1]); } // Last two verts should make first edge of next triangle __tess_idx[0] = __tess_idx[1]; __tess_idx[1] = idx; __tess_odd = !__tess_odd; } // TessParseTriangleXXX() //////////////////////////////////////// ////////////////////////////////////////////////////////////////// // // TessVertexCallback() // // - GLU will first call TessBeginCallback() to let us know how // verticies passed to this function should be interepreted. // Verticies are then passed in one at a time, so this // function keeps state to track the last two verts that make // up a full triangle as well as the winding order if the // verticies define a triangle strip. // ////// void APIENTRY TessVertexCallback(GLvoid *vertex) { // This is a bit of a hack, I assume the vertex // passed in is a pointer into the vertex array // we gave GLU, and I compute the index into // that array via simple pointer arithmetic. GLdouble *dpos = (GLdouble *) vertex; int vidx = (((unsigned char *)dpos) - ((unsigned char *)__verts)) / sizeof(VertexRec); if ( __tess_nidx == 2 ) { // If two verts were previously given, then this // vertex completes a triangle. switch (__tess_type) { case GL_TRIANGLES: TessParseTriangle(vidx); break; case GL_TRIANGLE_FAN: TessParseTriangleFan(vidx); break; case GL_TRIANGLE_STRIP: TessParseTriangleStrip(vidx); break; default: printf("Unknown tesselasion enum: %d\n", __tess_type); // Don't create triangles from these tesselations break; } } else { // All triangle tesselation types need at least two verts. __tess_idx[__tess_nidx] = vidx; __tess_nidx++; } } // TessVertexCallback() ////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // // TessWithGlu() // // - Function that uses GLU to triangulate a polygon defined by // lists of countours of its perimeter and any hole(s) it may // have. // ////// bool TessWithGlu(void) { // Allocate space for the triangle data. A polygon with N // verts will have N-2 trigs in the triangulation. If the // polygon has holes in it (Number of countours is > 1), // then it will have (N-2) + 2*(# countours - 1) instead. if ( __num_contours < 1 ) { return false; } __num_trigs = __num_verts - 2 + 2 * (__num_contours - 1); free(__trigs); __trigs = (Triangle *)malloc(__num_trigs * sizeof(TriangleRec)); if ( !__trigs ) { return false; } // Setup triangluation state __tess_type = GL_INVALID_ENUM; // No triangulation type defined. __tess_nidx = 0; // No vertex indices defined. __tess_num_trigs = 0; // No triangles triangulated. __tess_err = NULL; // No errors. // Create a GLU tesselator and setup our callbacks. GLUtesselator *tess = gluNewTess(); gluTessCallback(tess, GLU_TESS_BEGIN, (GLvoid (APIENTRY *) ()) &TessBeginCallback); gluTessCallback(tess, GLU_TESS_VERTEX, (GLvoid (APIENTRY *) ()) &TessVertexCallback); gluTessCallback(tess, GLU_TESS_ERROR, (GLvoid (APIENTRY *) ()) &TessErrorCallback); gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); gluTessNormal(tess, 0,0,1); // Polygon in XY-plane, +Z axis is 'up'. // Tesselate the polygon. First countour is outside perimeter of // polygon (clockwise vertex order), and all others define holes // in the polygon (counter clockwise vertex order). gluTessBeginPolygon(tess, NULL); Vertex *vert = __verts; for ( int c = 0; c < __num_contours; c++ ) { int num_verts = __contour_vert_count[c]; gluTessBeginContour(tess); for ( int i = 0; i < num_verts; i++ ) { gluTessVertex(tess, vert->dpos, vert->dpos); vert++; } gluTessEndContour(tess); } gluTessEndPolygon(tess); gluDeleteTess(tess); // Handle errors if ( __tess_err ) { fprintf(stderr, "Tessellation Error: %s\n", __tess_err); free(__trigs); __trigs = NULL; __num_trigs = 0; return false; } // Some sanity printf("Alloced %d trigs, tesselated %d.\n", __num_trigs, __tess_num_trigs) ; __num_trigs = __tess_num_trigs; return true; } // TessWithGlu() /////////////////////////////////////////////////