////////////////////////////////////////////////////////////////// // Robert C. Duvall // Fall 2001 // #include // also includes glu and gl correctly #include // for exit #include // for printf #include #include #include using namespace std; ////////////////////////////////////////////////////////////////// // Globals typedef GLfloat point[3]; // // Constants // const GLuint LIST_ID = 1; // 306 vertices #include "vertices.h" // 32 patches each defined by 16 vertices, arranged in a 4 x 4 array // NOTE: numbering scheme for teapot has vertices labeled from 1 to 306 // remnent of the days of FORTRAN #include "patches.h" // // Variables // GLenum g_toRender = GL_QUADS; bool g_isCompiled = false; bool g_isRecursive = false; int g_level; point g_data[32][4][4]; // size of glut window GLsizei theWindowWidth = 800; GLsizei theWindowHeight = 600; // number of frames per second int theFrameCount = 0; int theLastFrameTime = 0; GLfloat g_lookat[9] = { 0, 1, 8, // eye 0, 0, 0, // aim 0, 1, 0 }; // up ////////////////////////////////////////////////////////////////// // Utility functions // void computeFPS () // post: compute frames per second and display in window's title bar { theFrameCount++; int currentTime = glutGet(GLenum(GLUT_ELAPSED_TIME)); if (currentTime - theLastFrameTime > 1000) { char s[16]; sprintf(s, "FPS: %4.2f", theFrameCount * 1000.0 / (currentTime - theLastFrameTime)); glutSetWindowTitle(s); theLastFrameTime = currentTime; theFrameCount = 0; } } void updateCamera (GLfloat eye_x, GLfloat eye_y, GLfloat eye_z) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, GLfloat(theWindowWidth) / GLfloat(theWindowHeight), 0.1, 500.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt( g_lookat[0], g_lookat[1], g_lookat[2], g_lookat[3], g_lookat[4], g_lookat[5], g_lookat[6], g_lookat[7], g_lookat[8] ); } void normal (point n, point p, point q, point r) { n[0] = (q[1]-p[1])*(r[2]-p[2])-(q[2]-p[2])*(r[1]-p[1]); n[1] = (q[2]-p[2])*(r[0]-p[0])-(q[0]-p[0])*(r[2]-p[2]); n[2] = (q[0]-p[0])*(r[2]-p[2])-(q[2]-p[2])*(r[0]-p[0]); } /* transpose wastes time but makes program more readable */ void transpose (point a[4][4]) { for (int i = 0; i < 4; i++) { for (int j = i; j < 4; j++) { for(int k = 0; k < 3; k++) { GLfloat tt = a[i][j][k]; a[i][j][k] = a[j][i][k]; a[j][i][k] = tt; } } } } ////////////////////////////////////////////////////////////////// // User Functions // These are the functions you will mostly be changing. // void draw_patch (point p[4][4]) { point n; normal(n, p[0][0], p[3][0], p[3][3]); glBegin(g_toRender); glNormal3fv(n); glVertex3fv(p[0][0]); glVertex3fv(p[3][0]); glVertex3fv(p[3][3]); glVertex3fv(p[0][3]); glEnd(); } // division of convex hull of Bezier curve void divide_curve(point c[4], point r[4], point l[4]) { point t; for (int i = 0; i < 3; i++) { l[0][i] = c[0][i]; r[3][i] = c[3][i]; l[1][i] = (c[1][i] + c[0][i])/2; r[2][i] = (c[2][i] + c[3][i])/2; t[i] = (l[1][i] + r[2][i])/2; l[2][i] = (t[i] + l[1][i])/2; r[1][i] = (t[i] + r[2][i])/2; l[3][i] = r[0][i] = (l[2][i] + r[1][i])/2; } } // subdivide curves in u direction, transpose results, divide // in u direction again (equivalent to subdivision in v) void divide_patch(point p[4][4], int n) { if (n > 0) { point q[4][4], r[4][4], s[4][4], t[4][4]; point a[4][4], b[4][4]; for (int k = 0; k < 4; k++) { divide_curve(p[k], a[k], b[k]); } transpose(a); transpose(b); for (int k = 0; k < 4; k++) { divide_curve(a[k], q[k], r[k]); divide_curve(b[k], s[k], t[k]); } /* recursive division of 4 resulting patches */ divide_patch(q, n - 1); divide_patch(r, n - 1); divide_patch(s, n - 1); divide_patch(t, n - 1); } else { draw_patch(p); } } void initModel (int level) { // put teapot data into single array for subdivision for (int i = 0; i < 32; i++) { for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { int m = indices[i][j][k] - 1; for(int n = 0; n < 3; n++) { g_data[i][j][k][n] = vertices[m][n]; } } } } g_level = level; } void drawTeapot (int level) { // divide all 32 patches for (int i = 0; i < 32; i++) { divide_patch(g_data[i], level); } } void drawTeapot () { for (int k = 0; k < 32; k++) { glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &g_data[k][0][0][0]); glBegin(GL_LINES); for (int j = 0; j <= 8; j++) { for (int i = 0; i < 30; i++) { glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0); glEvalCoord2f((GLfloat)(i + 1)/30.0, (GLfloat)j/8.0); glEvalCoord2f((GLfloat)j/8.0, (GLfloat)(i + 1)/30.0); glEvalCoord2f((GLfloat)j/8.0, (GLfloat)i/30.0); } } glEnd(); } } void drawModel (int level) { // data aligned along z axis, rotate to align with y axis glRotatef(-90.0, 1.0,0.0, 0.0); glColor3f(1.0, 1.0, 1.0); if (g_isCompiled) { glCallList(LIST_ID); } else if (g_isRecursive) { drawTeapot(level); } else { glEnable(GL_MAP2_VERTEX_3); glEnable(GL_MAP2_NORMAL); glMapGrid2f(8, 0.0, 1.0, 8, 0.0, 1.0); drawTeapot(); } } ////////////////////////////////////////////////////////////////// // Callback Functions // These functions are registered with the glut window and called // when certain events occur. // void onInit (int level) { initModel(level); GLfloat mat_specular[]={1.0, 1.0, 1.0, 1.0}; GLfloat mat_diffuse[]={1.0, 1.0, 1.0, 1.0}; GLfloat mat_ambient[]={1.0, 1.0, 1.0, 1.0}; GLfloat mat_shininess={100.0}; GLfloat light_ambient[]={0.0, 0.0, 0.0, 1.0}; GLfloat light_diffuse[]={1.0, 1.0, 1.0, 1.0}; GLfloat light_specular[]={1.0, 1.0, 1.0, 1.0}; GLfloat light_position[]={10.0, 10.0, 10.0, 0.0}; glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialf(GL_FRONT, GL_SHININESS, mat_shininess); glShadeModel(GL_SMOOTH); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); // automatic normaization of normals glEnable(GL_CULL_FACE); // eliminate backfacing polygons glCullFace(GL_BACK); glClearColor (0.0, 0.0, 0.0, 1.0); } void onRedraw () { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); drawModel(g_level); glPopMatrix(); glutSwapBuffers(); } void onIdle () { computeFPS(); glutPostRedisplay(); } void onResize (int width, int height) { theWindowWidth = GLsizei(width); theWindowHeight = GLsizei(height); glViewport(0, 0, theWindowWidth, theWindowHeight); updateCamera(g_lookat[0], g_lookat[1], g_lookat[2]); } void onVisible (int state) { if (state == GLUT_VISIBLE) { glutIdleFunc(onIdle); } else { glutIdleFunc(NULL); } } void onKeyPressed (unsigned char key, int /* mouseX */, int /* mouseY */) { switch (key) { case 'c': glDeleteLists(LIST_ID, 1); glNewList(LIST_ID, GL_COMPILE); drawTeapot(g_level); glEndList(); g_isCompiled = true; break; case 'R': case 'r': g_isRecursive = ! g_isRecursive; g_isCompiled = false; break; case ' ': g_toRender = ((g_toRender == GL_QUADS) ? GL_LINES : GL_QUADS); g_isCompiled = false; break; case '{': case '[': if (g_level > 2) { g_level--; g_isCompiled = false; } break; case '}': case ']': if (g_level < 8) { g_level++; g_isCompiled = false; } break; case 'q': case 27: // ESCAPE key exit(1); break; } } void onSpecialKeyPressed (int key, int /* mouseX */, int /* mouseY */) { switch (key) { case GLUT_KEY_LEFT: g_lookat[0] += 0.2; break; case GLUT_KEY_RIGHT: g_lookat[0] -= 0.2; break; case GLUT_KEY_UP: g_lookat[1] += 0.2; break; case GLUT_KEY_DOWN: g_lookat[1] -= 0.2; break; case GLUT_KEY_PAGE_UP: g_lookat[2] -= 0.2; break; case GLUT_KEY_PAGE_DOWN: g_lookat[2] += 0.2; break; default: return; } updateCamera(g_lookat[0], g_lookat[1], g_lookat[2]); glutPostRedisplay(); } ////////////////////////////////////////////////////////////////// // Main Function // int main (int argc, char *argv[]) { glutInit(&argc, argv); glutInitWindowSize(theWindowWidth, theWindowHeight); glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB); glutCreateWindow(argv[0]); onInit((argc > 1) ? atoi(argv[1]) : 3); glutDisplayFunc(onRedraw); glutReshapeFunc(onResize); glutVisibilityFunc(onVisible); glutKeyboardFunc(onKeyPressed); glutSpecialFunc(onSpecialKeyPressed); glutMainLoop(); return 0; // program never gets here }