All callbacks are registered with the following common method:
void gluTessCallback(GLUtesselator* tObj, GLenum callbackType, void (*cbFcn)());
"callbackType" | expected "cbFcn" prototype |
GLU_TESS_BEGIN | void beginCB(GLenum mode); |
GLU_TESS_VERTEX | void vertexCB(void* pvas); |
GLU_TESS_END | void endCB(); |
GLU_TESS_ERROR | void errorCB(GLenum errorCode); |
As indicated in the table, most of the tessellation callbacks take one or more parameters; hence you will need to type-cast actual parameters. Assuming you have executed:
and assuming you have created the following typedef:
do as indicated in the following table:
Given function prototype | Register your callback handler as |
---|---|
void tessBeginCB(GLenum mode); | gluTessCallback(tObj,GLU_TESS_BEGIN, reinterpret_cast<parameterlessCallbackType>(tessBeginCB)); |
void tessVertexCB(void* pva); | gluTessCallback(tObj,GLU_TESS_VERTEX, reinterpret_cast<parameterlessCallbackType>(tessVertexCB)); |
void tessEndCB(); | gluTessCallback(tObj,GLU_TESS_END, tessEndCB); // No need for type-cast |
void tessErrorCB(GLenum e); | gluTessCallback(tObj,GLU_TESS_ERROR, reinterpret_cast<parameterlessCallbackType>(tessErrorCB)); |
If you want your callbacks to be methods of a class, they must be class (not instance) methods; i.e., they must be declared as "static" in the class definition. Hence:
Given class method declarations | Register your callback handler as |
---|---|
class SceneElement { … static void tessBeginCB(GLenum mode); static void tessVertexCB(void* pva); static void tessEndCB(); static void tessErrorCB(GLenum e); … }; |
gluTessCallback(tObj,GLU_TESS_BEGIN,
reinterpret_cast<parameterlessCallbackType>(SceneElement::tessBeginCB)); gluTessCallback(tObj,GLU_TESS_VERTEX, reinterpret_cast<parameterlessCallbackType>(SceneElement::tessVertexCB)); gluTessCallback(tObj,GLU_TESS_END, SceneElement::tessEndCB); // No need for type-cast gluTessCallback(tObj,GLU_TESS_ERROR, reinterpret_cast<parameterlessCallbackType>(SceneElement::tessErrorCB)); |
(Some of this material may make more sense after studying Step 3 of Part 1.)
When you call gluTessEndPolygon, the algorithm to do the tessellation starts, and it will begin calling your callbacks in sequences like:
yourTessBeginCB, yourTessVertexCB, …, yourTessVertexCB, yourTessEndCB
You will need to create two buffers: (i) a coordinate buffer to hold vertex coordinates as they are delivered to your vertex callback, and (ii) a buffer to hold the glDrawArrays data you will be recording as the callbacks are made.
The basic idea is the following:
When control returns to your code from the gluEndPolygon call, you need to copy the coordinate buffer data to the GPU in the usual way (i.e., using glGenBuffers-glBindBuffer-glBufferData along with the usual glVertexAttribPointer and glEnableVertexAttribArray calls). The "glDrawArrays buffer" you created will just remain a CPU-side data structure as one of your ModelView subclass instance variables and will be used as described in Part 2. It should look something like:
GL_TRIANGLE_STRIP | 0 | 20 |
GL_TRIANGLE_STRIP | 20 | 13 |
GL_TRIANGLES | 33 | 3 |
… | … | … |
Because we never make mistakes, there will be no need to worry about the GLU_TESS_ERROR callback.
Yeah, right.
Your error callback will be invoked if the algorithm detects a problem of some sort. The GLenum passed to it is an error code. There is a utility function for obtaining a string representation for any such error code. You can implement your error callback something like:
void tessErrorCB(GLenum e) { std::cerr << static_cast<const unsigned char*>(gluErrorString(e)) << std::endl; // On the most recent Mac OS release, "gluErrorString" seems to have disappeared. If you get compilation errors // related to this funtion, you will need to do instead: // std::cerr << static_cast<int>(e) << std::endl; }
In your GLU_TESS_VERTEX callback, you will need to cast the void* formal parameter that comes in:
void tessVertexCB(void* pvaAsVoid) { double* xyz = reinterpret_cast<double*> (pvaAsVoid); … }