/* A 2D Bezier surface. Actually, a selection of 3 surfaces, so this program isn't quite as simple as it could be. It also has a handful of extra lines of code for texture-mapping the patches. On the other hand, it always has u_order = v_order, so it's also not as general as it could be. Written by Scott D. Anderson scott.anderson@acm.org Fall 2007 */ #include #include // for atoi and exit #include int Order = 2; bool Wirep = true; bool ShowControlPointsp = true; bool Texturep = false; /* ================================================================================ Some control-point arrays for Bezier patches. All of them are flat (all Z components are zero), and symmetrical across both X and Y. The corners form an 8x4 rectangle. The order 4 and order 3 versions are roughly oval, with derivatives at the corner of 45 degrees; the order 2 is obviously a rectangle. */ GLfloat spatial_cp_order2[2*2*3] = { // top row, left to right -4, +2, 0, +4, +2, 0, -4, -2, 0, +4, -2, 0 }; GLfloat spatial_cp_order3[3*3*3] = { // top row, left to right -4, +2, 0, 0, +6, 0, +4, +2, 0, -6, 0, 0, 0, 0, 0, +6, 0, 0, -4, -2, 0, 0, -6, 0, +4, -2, 0 }; GLfloat spatial_cp_order4[4*4*3] = { // top row, left to right -4, +2, 0, -2, +4, 0, +2, +4, 0, +4, +2, 0 , -5, +1, 0, -2, +1, 0, +2, +1, 0, +5, +1, 0 , -5, -1, 0, -2, -1, 0, +2, -1, 0, +5, -1, 0 , -4, -2, 0, -2, -4, 0, +2, -4, 0, +4, -2, 0 }; GLfloat texture_cp[4*4*2] = { // top row, left to right 0, 0, 1, 0, 0, 1, 1, 1 }; /* ================================================================================ A generic array of control points, for passing into functions and such. */ GLfloat* control_points; /* Simple function to print the control points, to make sure we get the order and strides right, as well as all the numbers. Of course, an important purpose of this function is to demonstrate what the different strides mean. */ void printBezierControlPoints(int u_order, int u_stride, int v_order, int v_stride, GLfloat* cp) { // there are u_order * v_order control points in all. Use nested loops to iterate over them. for (int v=0; v < v_order; v++ ) { printf("Row %d:\n",v); for( int u=0; u < u_order; u++ ) { printf("\t(%d,%d) = (%4.2f,%4.2f,%4.2f)", u, v, // fancy pointer arithmetic: cp[u*u_stride+v*v_stride+0], cp[u*u_stride+v*v_stride+1], cp[u*u_stride+v*v_stride+2]); } printf("\n"); } } /* Graphically display the control points. The "u" dimension is red, and the "v" dimension is green, so the first CP is blue and the last is white. */ void drawBezierControlPoints(int u_order, int u_stride, int v_order, int v_stride, GLfloat* cp) { glPushAttrib(GL_CURRENT_BIT); // so that the current color is not changed. // map from int in [0,order-1] to float in [0.0,1.0] float u_factor = 1.0/(u_order-1); float v_factor = 1.0/(v_order-1); glPointSize(6); glBegin(GL_POINTS); // there are u_order * v_order control points in all. Use nested loops to iterate over them. for (int v=0; v < v_order; v++ ) { printf("Row %d:\n",v); for( int u=0; u < u_order; u++ ) { printf("\t%f %f", u*u_factor , v*v_factor); glColor3f( u*u_factor , v*v_factor, 1); // compute color of this vertex glVertex3fv(cp+u*u_stride+v*v_stride); // fancy pointer arithmetic. } printf("\n"); } glEnd(); glPopAttrib(); } /* The following doesn't actually draw the patch, but it sets up drawing of the whole bezier patch. Other arguments are as for glMap2f. */ void setupBezierPatch(int u_order, int u_stride, int v_order, int v_stride, GLfloat* cp) { const float umin = 0; const float umax = 1; const float vmin = 0; const float vmax = 1; glMap2f(GL_MAP2_VERTEX_3, umin, umax, u_stride, u_order, vmin, vmax, v_stride, v_order, cp); glEnable(GL_MAP2_VERTEX_3); twError(); } void display(void) { twError(); twDisplayInit(); twCamera(); glPushAttrib(GL_ALL_ATTRIB_BITS); glColor3f(0,1,0); // green patch if(ShowControlPointsp) drawBezierControlPoints(Order,3,Order,3*Order,control_points); setupBezierPatch(Order,3,Order,3*Order,control_points); if( Texturep ) { glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, texture_cp); glEnable(GL_MAP2_TEXTURE_COORD_2); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); } else { glDisable(GL_TEXTURE_2D); } twError(); const int steps = 16; glMapGrid2f(steps,0,1,steps,0,1); glEvalMesh2( Wirep ? GL_LINE : GL_FILL, 0, steps, 0, steps ); twError(); glPopAttrib(); glFlush(); glutSwapBuffers(); } void setOrder(unsigned char key, int x, int y) { switch(key) { case '2': Order = 2; control_points = spatial_cp_order2; break; case '3': Order = 3; control_points = spatial_cp_order3; break; case '4': Order = 4; control_points = spatial_cp_order4; break; } printBezierControlPoints(Order,3,Order,3*Order,control_points); glutPostRedisplay(); } void toggles(unsigned char key, int x, int y) { switch(key) { case 'w': Wirep = !Wirep; break; case 'c': ShowControlPointsp = !ShowControlPointsp; break; case 't': Texturep = !Texturep; break; } glutPostRedisplay(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); twInitWindowSize(500,500); glutCreateWindow(argv[0]); twError(); glutDisplayFunc(display); twError(); twUSflag(); twBoundingBox(-4,+4, -2,+2, -1,1); twMainInit(); twKeyCallback('2',setOrder,"order 2 (linear)"); twKeyCallback('3',setOrder,"order 3 (quadratic)"); twKeyCallback('4',setOrder,"order 4 (cubic)"); twKeyCallback('w',toggles,"toggle wireframe"); twKeyCallback('c',toggles,"toggle showing control points"); twKeyCallback('t',toggles,"toggle texturing"); setOrder('2',0,0); // default to linear bezier patch glutMainLoop(); return 0; }