b = pointTimes(3*pow(u,2)*(1-u), p[1]);
c = pointTimes(3*u*pow((1-u),2), p[2]);
d = pointTimes(pow((1-u),3), p[3]);
r = pointAdd(pointAdd(a, b), pointAdd(c, d));
return r;
}
This function does the lion's share of the work by generating all the triangle strips and storing them in a display list. We do this so that we don't have to recalculate the patch each frame, only when it changes. By the way, a cool effect you might want to try might be to use the morphing tutorial to morph the patch's control points. This would yeild a very cool smooth, organic, morphing effect for relatively little overhead (you only morph 16 points, but you have to recalculate). The "last" array is used to keep the previous line of points (since a triangle strip needs both rows). Also, texture coordinates are calculated by using the u and v values as the percentages (planar mapping).
One thing we don't do is calculate the normals for lighting. When it comes to this, you basically have two options. The first is to find the center of each triangle, then use a bit of calculus and calculate the tangent on both the x and y axes, then do the cross product to get a vector perpendicular to both, THEN normalize the vector and use that as the normal. OR (yes, there is a faster way) you can cheat and just use the normal of the triangle (calculated your favorite way) to get a pretty good approximation. I prefer the latter; the speed hit, in my opinion, isn't worth the extra little bit of realism.
// Generates A Display List Based On The Data In The Patch
// And The Number Of Divisions
GLuint genBezier(BEZIER_PATCH patch, int divs) {
int u = 0, v;
float py, px, pyold;
GLuint drawlist = glGenLists(1); // Make The Display List
POINT_3D temp[4];
POINT_3D *last = (POINT_3D*)malloc(sizeof(POINT_3D)*(divs+1));
// Array Of Points To Mark The First Line Of Polys
if (patch.dlBPatch != NULL) // Get Rid Of Any Old Display Lists
glDeleteLists(patch.dlBPatch, 1);
temp[0] = patch.anchors[0][3]; // The First Derived Curve (Along X-Axis)
temp[1] = patch.anchors[1][3];
temp[2] = patch.anchors[2][3];
temp[3] = patch.anchors[3][3];
for (v=0;v<=divs;v++) { // Create The First Line Of Points
px = ((float)v)/((float)divs); // Percent Along Y-Axis
// Use The 4 Points From The Derived Curve To Calculate The Points Along That Curve
last[v] = Bernstein(px, temp);
}
glNewList(drawlist, GL_COMPILE); // Start A New Display List
glBindTexture(GL_TEXTURE_2D, patch.texture); // Bind The Texture
for (u=1;u<=divs;u++) {
py = ((float)u)/((float)divs); // Percent Along Y-Axis
pyold = ((float)u-1.0f)/((float)divs); // Percent Along Old Y Axis
temp[0] = Bernstein(py, patch.anchors[0]); // Calculate New Bezier Points
temp[1] = Bernstein(py, patch.anchors[1]);
temp[2] = Bernstein(py, patch.anchors[2]);
temp[3] = Bernstein(py, patch.anchors[3]);
glBegin(GL_TRIANGLE_STRIP); // Begin A New Triangle Strip
for (v=0;v<=divs;v++) {
px = ((float)v)/((float)divs); // Percent Along The X-Axis
glTexCoord2f(pyold, px); // Apply The Old Texture Coords
glVertex3d(last[v].x, last[v].y, last[v].z); // Old Point
last[v] = Bernstein(px, temp); // Generate New Point
glTexCoord2f(py, px); // Apply The New Texture Coords
glVertex3d(last[v].x, last[v].y, last[v].z); // New Point
}
glEnd(); // END The Triangle Strip
}
glEndList(); // END The List
free(last); // Free The Old Vertices Array
return drawlist; // Return The Display List
}
Here we're just loading the matrix with some values I've picked that I think look cool. Feel free to screw around with these and see what it looks like. :-)
void initBezier(void) {
mybezier.anchors[0][0] = makePoint(-0.75, –0.75, –0.50); // Set The Bezier Vertices
mybezier.anchors[0][1] = makePoint(-0.25, –0.75, 0.00);
mybezier.anchors[0][2] = makePoint( 0.25, –0.75, 0.00);
mybezier.anchors[0][3] = makePoint( 0.75, –0.75, –0.50);
mybezier.anchors[1][0] = makePoint(-0.75, –0.25, –0.75);
mybezier.anchors[1][1] = makePoint(-0.25, –0.25, 0.50);
mybezier.anchors[1][2] = makePoint( 0.25, –0.25, 0.50);
mybezier.anchors[1][3] = makePoint( 0.75, –0.25, –0.75);
mybezier.anchors[2][0] = makePoint(-0.75, 0.25, 0.00);
mybezier.anchors[2][1] = makePoint(-0.25, 0.25, –0.50);
mybezier.anchors[2][2] = makePoint( 0.25, 0.25, –0.50);
mybezier.anchors[2][3] = makePoint( 0.75, 0.25, 0.00);
mybezier.anchors[3][0] = makePoint(-0.75, 0.75, –0.50);
mybezier.anchors[3][1] = makePoint(-0.25, 0.75, –1.00);
mybezier.anchors[3][2] = makePoint( 0.25, 0.75, –1.00);
mybezier.anchors[3][3] = makePoint( 0.75, 0.75, –0.50);
mybezier.dlBPatch = NULL; // Go Ahead And Initialize This To NULL
}
This is basically just an optimised routine to load a single bitmap. It can easily be used to load an array of em just by putting it in a simple loop.
// Load Bitmaps And Convert To Textures
BOOL LoadGLTexture(GLuint *texPntr, char* name) {
BOOL success = FALSE;
AUX_RGBImageRec *TextureImage = NULL;
glGenTextures(1, texPntr); // Generate 1 Texture
FILE* test=NULL;
TextureImage = NULL;
test = fopen(name, "r"); // Test To See If The File Exists
if (test != NULL) { // If It Does
fclose(test); // Close The File
TextureImage = auxDIBImageLoad(name); // And Load The Texture
}
if (TextureImage != NULL) { // If It Loaded
success = TRUE;
// Typical Texture Generation Using Data From The Bitmap
glBindTexture(GL_TEXTURE_2D, *texPntr);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
if (TextureImage->data)
free(TextureImage->data);
return success;
}
Just adding the patch initialization here. You would do this whenever you create a patch. Again, this might be a cool place to use C++ (bezier class?).
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping