/* cs4451_a2.cpp */ /* * Jose Caban * gtg184g * proj2 */ #include #include #include #include #include #ifdef _OS_LINUX_ #include #include #endif //Windows only needs glut.h care of SGI ;) #include #define VPD_MIN 200 #define VPD_DEFAULT 800 #define VPD_MAX 1024 enum MENU { MENU_SWITCH_SHADING=0, MENU_ZOOM_IN, MENU_ZOOM_OUT, MENU_POINTS, MENU_LINES, MENU_TRIANGLES, MENU_SWITCH_LIGHT, MENU_SWITCH_CULLING, MENU_TURN_OFF_CULLING, MENU_TURN_ON_CULLING, MENU_RESET_MODELVIEW }; #define TWOPI (2.0 * M_PI) /////////////////////////////////////////////////////////////////// //Define the Normal type typedef struct SNormal { GLfloat i; GLfloat j; GLfloat k; } SNormal; typedef struct SPoint { GLfloat x; GLfloat y; GLfloat z; } SPoint; //Define the Vertex type for use in keeping track of Vertices typedef struct SVertex { SNormal normal; GLfloat x; GLfloat y; GLfloat z; } SVertex; //Define the Data structure that will hold all the vertices typedef struct SVertexList { SVertex* vertices; int length; } SVertexList; //Define a Triangle (a1,a2,a3 are indices to the SVertexList typedef struct STriangle { SNormal normal; GLint a1,a2,a3; } STriangle; typedef struct STriangleList { STriangle* triangles; GLint length; } STriangleList; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////// // Globals SVertexList vertexList; STriangleList triList; GLfloat currModelTransform[16]; GLint wid; /* GLUT window id */ GLint vpd = VPD_DEFAULT; /* (square) viewport dimensions */ GLuint sceneID; /* display list ID */ GLuint gouraudID; /* gouraud display list ID */ GLuint flatID; /* flat display list ID */ GLfloat angle1 = 0; /* angles used in animation */ GLfloat angle2 = 0; GLfloat dangle1 = 0.57; GLfloat dangle2 = 0.71; GLdouble viewDist = 8.0; /* Changes the view distance view */ GLuint cullMode = GL_BACK; GLboolean worldLight = GL_TRUE; /////////////////////////////////////////////////////// /////////////////////////////////////////////////////// //Functions /* * Input scanner */ void scanInput(GLvoid); /* * Initialize the Light Source */ GLvoid init_lightsource ( GLvoid ) { GLfloat light_ambient[] = { .1, .1, .1, 1.0 }; GLfloat light_diffuse[] = { .9, .9, .9, 1.0 }; GLfloat light_specular[] = { 0, 0, 0, 1.0 }; GLfloat light_position[] = { 2.0, 2.0, 2.0, 0.0 }; glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightf(GL_LIGHT0,GL_CONSTANT_ATTENUATION,1.0); glLightf(GL_LIGHT0,GL_LINEAR_ATTENUATION,0.0); glLightf(GL_LIGHT0,GL_QUADRATIC_ATTENUATION,0.0); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } GLvoid set_material_properties ( GLfloat r, GLfloat g, GLfloat b ) { GLfloat mat_specular[4] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat mat_ambient_and_diffuse[4] = { 0.5, 0.5, 0.5, 1.0 }; GLfloat mat_shininess[1] = { 0.0 }; mat_specular[0] = mat_ambient_and_diffuse[0] = r; mat_specular[1] = mat_ambient_and_diffuse[1] = g; mat_specular[2] = mat_ambient_and_diffuse[2] = b; glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_ambient_and_diffuse); } inline void cross(SNormal& dest, SVertex& a, SVertex& b) { dest.i = ( (a.y*b.z) - (a.z*b.y) ); //i dest.j = ( (a.z*b.x) - (a.x*b.z) ); //j dest.k = ( (a.x*b.y) - (a.y*b.x) ); //k } /* * Initialize the scene * */ GLuint init_flat_scene ( ) { GLuint sceneList = glGenLists(1); SNormal tempNormal; SVertex a1, a2, a3; glNewList(sceneList,GL_COMPILE); glBegin(GL_TRIANGLES); for (int i = 0; i < triList.length; i++) { a1 = vertexList.vertices[triList.triangles[i].a1]; a2 = vertexList.vertices[triList.triangles[i].a2]; a3 = vertexList.vertices[triList.triangles[i].a3]; tempNormal = triList.triangles[i].normal; glNormal3f(tempNormal.i, tempNormal.j, tempNormal.k); glVertex3f(a1.x, a1.y, a1.z); glVertex3f(a2.x, a2.y, a2.z); glVertex3f(a3.x, a3.y, a3.z); } glEnd(); glEndList(); return sceneList; } /* * Initialize the gouraud scene */ GLuint init_gouraud_scene ( ) { GLuint sceneList = glGenLists(1); SNormal tempNormal; SVertex a1, a2, a3; glNewList(sceneList,GL_COMPILE); glBegin(GL_TRIANGLES); for (int i = 0; i < triList.length; i++) { a1 = vertexList.vertices[triList.triangles[i].a1]; a2 = vertexList.vertices[triList.triangles[i].a2]; a3 = vertexList.vertices[triList.triangles[i].a3]; tempNormal = a1.normal; glNormal3f(tempNormal.i, tempNormal.j, tempNormal.k); glVertex3f(a1.x, a1.y, a1.z); tempNormal = a2.normal; glNormal3f(tempNormal.i, tempNormal.j, tempNormal.k); glVertex3f(a2.x, a2.y, a2.z); tempNormal = a3.normal; glNormal3f(tempNormal.i, tempNormal.j, tempNormal.k); glVertex3f(a3.x, a3.y, a3.z); } glEnd(); glEndList(); return sceneList; } GLuint create_scene(GLuint triangleList) { GLuint sceneList = glGenLists(1); glNewList(sceneList,GL_COMPILE); set_material_properties(1.0,1.0,1.0); /* Correctly size the object */ assert(vertexList.length > 0); SVertex Max; SVertex Min; SVertex Center; Max.x = Min.x = vertexList.vertices[0].x; Max.y = Min.y = vertexList.vertices[0].y; Max.z = Min.z = vertexList.vertices[0].z; for (int i = 1; i < vertexList.length; i++) { if (vertexList.vertices[i].x > Max.x) { Max.x = vertexList.vertices[i].x; } else if (vertexList.vertices[i].x < Min.x) { Min.x = vertexList.vertices[i].x; } if (vertexList.vertices[i].y > Max.y) { Max.y = vertexList.vertices[i].y; } else if (vertexList.vertices[i].y < Min.y) { Min.y = vertexList.vertices[i].y; } if (vertexList.vertices[i].z > Max.z) { Max.z = vertexList.vertices[i].z; } else if (vertexList.vertices[i].z < Min.z) { Min.z = vertexList.vertices[i].z; } } Center.x = (Max.x + Min.x) / 2.; Center.y = (Max.y + Min.y) / 2.; Center.z = (Max.z + Min.z) / 2.; float scaleX, scaleY, scaleZ; scaleX = (Max.x - Min.x) / 2.; scaleY = (Max.y - Min.y) / 2.; scaleZ = (Max.z - Min.z) / 2.; float scaleFactor; scaleFactor = scaleX > scaleY ? scaleX : scaleY; scaleFactor = scaleZ > scaleFactor ? scaleZ : scaleFactor; scaleFactor = 1. / scaleFactor; //printf("ScaleFactor: %f\n",scaleFactor); glPushMatrix(); glTranslatef(0.-(Center.x*scaleFactor),0.-(Center.y*scaleFactor),0.-(Center.z*scaleFactor)); glScalef(scaleFactor,scaleFactor,scaleFactor); glCallList(triangleList); glPopMatrix(); glEndList(); return sceneList; } GLuint create_flat_scene ( ) { return create_scene( init_flat_scene() ); } GLuint create_gouraud_scene ( ) { return create_scene( init_gouraud_scene() ); } /* --------------------------------------------- */ /* redraw the scene */ GLvoid draw(GLvoid) { /* set the projection matrix */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(viewDist,1.0,15.0,25.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* initialize light */ if (worldLight) { //World Lighting init_lightsource(); glTranslatef(0,0,-20); glMultMatrixf(currModelTransform); } else { //Model Lighting glTranslatef(0,0,-20); glMultMatrixf(currModelTransform); init_lightsource(); } /* ensure we're drawing to the correct GLUT window */ glutSetWindow(wid); /* clear the color buffers */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* DRAW WHAT IS IN THE DISPLAY LIST */ glCallList(sceneID); /* flush the pipeline */ glFlush(); /* look at our handiwork */ glutSwapBuffers(); } /* --------------------------------------------- */ /* handle mouse events */ SPoint oldPoint; //stores the previous point used in the virtual trackball GLvoid mouse_button(GLint btn, GLint state, GLint mx, GLint my) { switch( btn ) { case GLUT_LEFT_BUTTON: switch( state ) { case GLUT_DOWN: oldPoint.x = mx; oldPoint.y = my; oldPoint.z = -1; break; case GLUT_UP: break; } break; case GLUT_MIDDLE_BUTTON: switch( state ) { case GLUT_DOWN: break; case GLUT_UP: break; } break; case GLUT_RIGHT_BUTTON: switch( state ) { case GLUT_DOWN: break; case GLUT_UP: break; } break; } } inline void scalePoint(SPoint& p, GLint mx, GLint my) { //scale to -1 and 1 float scale = ceil(vpd/2.); p.x = (mx / scale) -1.; p.y = (my / scale) -1.; p.y*=-1.; //handle reversed y p.z = 1.-pow(p.x,2)-pow(p.y,2); if (p.z < 0) { float under = sqrt((p.x*p.x)+(p.y*p.y)); p.x /= under; p.y /= under; p.z = 0; } else { p.z = sqrt(p.z); } } inline GLfloat vecLength(SPoint& p) { return sqrt(pow(p.x,2)+pow(p.y,2)+pow(p.z,2)); } inline GLfloat dot(SPoint &a, SPoint &b) { return (a.x*b.x)+(a.y*b.y)+(a.z*b.z); } inline void cross(SPoint &dest, SPoint &a, SPoint &b) { dest.x = ( (a.y*b.z) - (a.z*b.y) ); //i dest.y = ( (a.z*b.x) - (a.x*b.z) ); //j dest.z = ( (a.x*b.y) - (a.y*b.x) ); //k } inline void calcAngle(GLfloat *newMatrix, GLfloat angle, SPoint& a) { newMatrix[0] = 1.+(1.-cos(angle))*(pow(a.x,2)-1.); newMatrix[1] = a.z*sin(angle) + (1.-cos(angle)) * a.x * a.y; newMatrix[2] = -1. * a.y * sin(angle) + (1.-cos(angle))*a.x*a.z; newMatrix[3] = 0; newMatrix[4] = -1. * a.z * sin(angle) + (1.-cos(angle)) * a.x * a.y; newMatrix[5] = 1. + (1. - cos(angle))*(pow(a.y,2)-1.); newMatrix[6] = a.x * sin(angle) + (1.-cos(angle))*a.y*a.z; newMatrix[7] = 0; newMatrix[8] = a.y * sin(angle) + (1.-cos(angle))*a.x * a.z; newMatrix[9] = -1. * a.x * sin(angle) + (1.-cos(angle))*a.y*a.z; newMatrix[10] = 1. + (1.-cos(angle))*(pow(a.z,2)-1.); newMatrix[11] = newMatrix[12] = newMatrix[13] = newMatrix[14] = 0; newMatrix[15] = 1.; } GLvoid button_motion(GLint mx, GLint my) { /* First calculate P */ static SPoint v; SPoint p, w, a; //Give us the scaled p on the sphere scalePoint(p,mx,my); //get the unit vector of p GLfloat length = vecLength(p); if (length==0) { return; } else { w.x = p.x / length; w.y = p.y / length; w.z = p.z / length; } //This is the first time the movement has been made if (oldPoint.z == -1) { scalePoint(oldPoint,(int)oldPoint.x,(int)oldPoint.y); length = vecLength(oldPoint); if (length==0) { return; } else { v.x = oldPoint.x / length; v.y = oldPoint.y / length; v.z = oldPoint.z / length; } } cross(a,oldPoint,p); length = vecLength(a); if (length==0) { return; } else { a.x /= length; a.y /= length; a.z /= length; } GLfloat angle = dot(v,w); if (angle > 1) { angle = 1; } else if (angle < -1) { angle = -1; } angle = acos(angle); /* Perform the rotation calculation */ GLfloat newMatrix[16]; calcAngle(newMatrix,angle,a); /* Make OpenGL do our work */ glPushMatrix(); glLoadMatrixf(newMatrix); glMultMatrixf(currModelTransform); glGetFloatv(GL_MODELVIEW_MATRIX,currModelTransform); glMatrixMode(GL_MODELVIEW); glPopMatrix(); oldPoint = p; v = w; glutPostRedisplay(); return; } GLvoid passive_motion(GLint mx, GLint my) { return; } /* --------------------------------------------- */ /* handle keyboard events; here, just exit if ESC is hit */ GLvoid keyboard(GLubyte key, GLint x, GLint y) { switch(key) { case 27: /* ESC */ exit(0); case 'w': viewDist -= .1; glutPostRedisplay(); break; case 's': viewDist += .1; glutPostRedisplay(); break; case 'a': currModelTransform[12] -= .01; glutPostRedisplay(); break; case 'd': currModelTransform[12] += .01; glutPostRedisplay(); break; default: break; } } /* --------------------------------------------- */ GLvoid menu ( int value ) { switch(value) { case MENU_SWITCH_SHADING: if (sceneID == gouraudID) { glShadeModel(GL_FLAT); sceneID = flatID; } else { glShadeModel(GL_SMOOTH); sceneID = gouraudID; } glutPostRedisplay(); break; case MENU_ZOOM_IN: viewDist -= 1.; glutPostRedisplay(); break; case MENU_ZOOM_OUT: viewDist += 1.; glutPostRedisplay(); break; case MENU_POINTS: glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); glutPostRedisplay(); break; case MENU_LINES: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glutPostRedisplay(); break; case MENU_TRIANGLES: glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glutPostRedisplay(); break; case MENU_SWITCH_CULLING: if (cullMode == GL_FRONT) { glCullFace(GL_BACK); cullMode = GL_BACK; } else { glCullFace(GL_FRONT); cullMode = GL_FRONT; } glutPostRedisplay(); break; case MENU_TURN_ON_CULLING: glEnable(GL_CULL_FACE); glutPostRedisplay(); break; case MENU_TURN_OFF_CULLING: glDisable(GL_CULL_FACE); glutPostRedisplay(); break; case MENU_SWITCH_LIGHT: worldLight = !worldLight; glutPostRedisplay(); break; case MENU_RESET_MODELVIEW: glPushMatrix(); glLoadIdentity(); glGetFloatv(GL_MODELVIEW_MATRIX,currModelTransform); glPopMatrix(); glutPostRedisplay(); default: break; } } /* --------------------------------------------- */ /* handle resizing the glut window */ GLvoid reshape(GLint vpw, GLint vph) { glutSetWindow(wid); /* maintain a square viewport, not too small, not too big */ if( vpw < vph ) vpd = vph; else vpd = vpw; if( vpd < VPD_MIN ) vpd = VPD_MIN; if( vpd > VPD_MAX ) vpd = VPD_MAX; glViewport(0, 0, vpd, vpd); glutReshapeWindow(vpd, vpd); glutPostRedisplay(); } /* --------------------------------------------- */ GLint init_glut(GLint *argc, char **argv) { GLint id; glutInit(argc,argv); /* size and placement hints to the window system */ glutInitWindowSize(vpd, vpd); glutInitWindowPosition(10,10); /* double buffered, RGB color mode */ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); /* create a GLUT window (not drawn until glutMainLoop() is entered) */ id = glutCreateWindow("cs4451 assignment 2"); /* register callbacks */ /* window size changes */ glutReshapeFunc(reshape); /* keypress handling when the current window has input focus */ glutKeyboardFunc(keyboard); /* mouse event handling */ glutMouseFunc(mouse_button); /* button press/release */ glutMotionFunc(button_motion); /* mouse motion w/ button down */ glutPassiveMotionFunc(passive_motion); /* mouse motion with button up */ /* window obscured/revealed event handler */ glutVisibilityFunc(NULL); /* handling of keyboard SHIFT, ALT, CTRL keys */ glutSpecialFunc(NULL); /* what to do when mouse cursor enters/exits the current window */ glutEntryFunc(NULL); /* what to do on each display loop iteration */ glutDisplayFunc(draw); /* Create the menu */ //Create the Zoom Menu GLint zoomMenu = glutCreateMenu(menu); glutAddMenuEntry("Zoom In (w)",MENU_ZOOM_IN); glutAddMenuEntry("Zoom Out (s)",MENU_ZOOM_OUT); //Create the Render Mode Menu GLint renderMenu = glutCreateMenu(menu); glutAddMenuEntry("Points Only",MENU_POINTS); glutAddMenuEntry("Lines Only",MENU_LINES); glutAddMenuEntry("Full Model",MENU_TRIANGLES); //Create the Culling Menu GLint cullingMenu = glutCreateMenu(menu); glutAddMenuEntry("Switch Culling Mode",MENU_SWITCH_CULLING); glutAddMenuEntry("Turn on Culling",MENU_TURN_ON_CULLING); glutAddMenuEntry("Turn off Culling",MENU_TURN_OFF_CULLING); GLint menuID = glutCreateMenu(menu); glutAddMenuEntry("Switch Shading Mode",MENU_SWITCH_SHADING); glutAddMenuEntry("Toggle World/Model Relative Light",MENU_SWITCH_LIGHT); glutAddSubMenu("Zoom",zoomMenu); glutAddSubMenu("Render", renderMenu); glutAddSubMenu("Culling", cullingMenu); glutAddMenuEntry("Reset View Model",MENU_RESET_MODELVIEW); glutSetMenu(menuID); glutAttachMenu(GLUT_RIGHT_BUTTON); return id; } /* --------------------------------------------- */ GLvoid init_opengl(GLvoid) { /* back-face culling on */ glEnable(GL_CULL_FACE); glCullFace(GL_BACK); /* We want to cull the back */ glFrontFace(GL_CW); /* The vertices we get are clockwise */ /* automatically scale normals to unit length after transformation */ glEnable(GL_NORMALIZE); /* clear to BLACK */ glClearColor(0.0, 0.0, 0.0, 1.0); /* Enable depth test */ glEnable(GL_DEPTH_TEST); } /* --------------------------------------------- */ void generateNormals() { /* Generate normals for triangles and Vertices */ SNormal tempNormal; SVertex a1a2, a1a3; SVertex a1, a2, a3; for (int i = 0; i < triList.length; i++) { a1 = vertexList.vertices[triList.triangles[i].a1]; a2 = vertexList.vertices[triList.triangles[i].a2]; a3 = vertexList.vertices[triList.triangles[i].a3]; a1a2.x = a2.x - a1.x; a1a2.y = a2.y - a1.y; a1a2.z = a2.z - a1.z; a1a3.x = a3.x - a1.x; a1a3.y = a3.y - a1.y; a1a3.z = a3.z - a1.z; cross(tempNormal,a1a2,a1a3); tempNormal.i *= -1; tempNormal.j *= -1; tempNormal.k *= -1; /* Store the normal for the triangle */ triList.triangles[i].normal = tempNormal; /* Add this normal to the normal of all the vertices */ vertexList.vertices[triList.triangles[i].a1].normal.i += tempNormal.i; vertexList.vertices[triList.triangles[i].a1].normal.j += tempNormal.j; vertexList.vertices[triList.triangles[i].a1].normal.k += tempNormal.k; vertexList.vertices[triList.triangles[i].a2].normal.i += tempNormal.i; vertexList.vertices[triList.triangles[i].a2].normal.j += tempNormal.j; vertexList.vertices[triList.triangles[i].a2].normal.k += tempNormal.k; vertexList.vertices[triList.triangles[i].a3].normal.i += tempNormal.i; vertexList.vertices[triList.triangles[i].a3].normal.j += tempNormal.j; vertexList.vertices[triList.triangles[i].a3].normal.k += tempNormal.k; } } /* --------------------------------------------- */ GLint main(GLint argc, char **argv) { /* Scan Input */ scanInput(); generateNormals(); /* initialize light */ init_lightsource(); /* Initialize the currentTransformation */ currModelTransform[0] = 1; currModelTransform[5] = 1; currModelTransform[10] = 1; currModelTransform[15] = 1; /* initialize GLUT: register callbacks, etc */ wid = init_glut(&argc, argv); /* any OpenGL state initialization we need to do */ init_opengl(); /* CREATE THE DISPLAY LIST FOR THE SCENE */ flatID = create_flat_scene(); gouraudID = create_gouraud_scene(); sceneID = gouraudID; glutMainLoop(); return 0; } void scanInput(GLvoid) { char input[81]; char *inputPtr; int i; //Get the # of vertices and # of triangles std::cin.getline(input,81); triList.length = strtol(input,&inputPtr,0); triList.triangles = (STriangle*)malloc(sizeof(STriangle)*triList.length); vertexList.length = strtol(inputPtr,NULL,0); vertexList.vertices = (SVertex*)malloc(sizeof(SVertex)*vertexList.length); //Skip over the first newline std::cin.getline(input,81); for (i=0; i < triList.length; i++) { std::cin.getline(input,81); //get points triList.triangles[i].a1 = strtol(input,&inputPtr,0); triList.triangles[i].a2 = strtol(inputPtr,&inputPtr,0); triList.triangles[i].a3 = strtol(inputPtr,NULL,0); triList.triangles[i].normal.i = 0; triList.triangles[i].normal.j = 0; triList.triangles[i].normal.k = 0; } //skip over separation newline std::cin.getline(input,81); for (i=0; i < vertexList.length; i++) { std::cin.getline(input,81); //get coordinates vertexList.vertices[i].x = strtod(input,&inputPtr); vertexList.vertices[i].y = strtod(inputPtr,&inputPtr); vertexList.vertices[i].z = strtod(inputPtr,NULL); vertexList.vertices[i].normal.i = 0; vertexList.vertices[i].normal.j = 0; vertexList.vertices[i].normal.k = 0; } }