/* smooth.c Nate Robins, 1997 Model viewer program. Excercises the glm library. */ #include #include #include #include #include #include #include "tb.h" #include "glm.h" GLuint model_list = 0; /* display list for object */ char* model_file = NULL; /* name of the obect file */ GLboolean facet_normal = GL_FALSE; /* draw with facet normal? */ GLMmodel* model; GLfloat smoothing_angle = 90.0; /* smoothing angle */ GLfloat scale; /* scaling factor */ GLdouble pan_x = 0.0; GLdouble pan_y = 0.0; GLdouble pan_z = 0.0; GLint mouse_state = -1; GLint mouse_button = -1; GLboolean bounding_box = GL_FALSE; GLboolean performance = GL_FALSE; GLboolean stats = GL_FALSE; GLfloat weld_distance = 0.00001; GLuint material_mode = 0; /* text: general purpose text routine. draws a string according to * format in a stroke font at x, y after scaling it by the scale * specified (scale is in window-space (lower-left origin) pixels). * * x - position in x (in window-space) * y - position in y (in window-space) * scale - scale in pixels * format - as in printf() */ void text(GLuint x, GLuint y, GLfloat scale, char* format, ...) { va_list args; char buffer[255], *p; GLfloat font_scale = 119.05 + 33.33; va_start(args, format); vsprintf(buffer, format, args); va_end(args); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT)); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glTranslatef(x, y, 0.0); glScalef(scale/font_scale, scale/font_scale, scale/font_scale); for(p = buffer; *p; p++) glutStrokeCharacter(GLUT_STROKE_ROMAN, *p); glPopAttrib(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } void lists(void) { GLfloat ambient[] = { 0.2, 0.2, 0.2, 1.0 }; GLfloat diffuse[] = { 0.8, 0.8, 0.8, 1.0 }; GLfloat specular[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat shininess = 65.0; if (model_list) glDeleteLists(model_list, 1); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess); /* generate a list */ if (material_mode == 0) { if (facet_normal) model_list = glmList(model, GLM_FLAT); else model_list = glmList(model, GLM_SMOOTH); } else if (material_mode == 1) { if (facet_normal) model_list = glmList(model, GLM_FLAT | GLM_COLOR); else model_list = glmList(model, GLM_SMOOTH | GLM_COLOR); } else if (material_mode == 2) { if (facet_normal) model_list = glmList(model, GLM_FLAT | GLM_MATERIAL); else model_list = glmList(model, GLM_SMOOTH | GLM_MATERIAL); } } void init(void) { tbInit(GLUT_MIDDLE_BUTTON); /* read in the model */ model = glmReadOBJ(model_file); scale = glmUnitize(model); glmFacetNormals(model); glmVertexNormals(model, smoothing_angle); if (model->nummaterials > 0) material_mode = 2; /* create new display lists */ lists(); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } void reshape(int width, int height) { tbReshape(width, height); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)height / (GLfloat)width, 1.0, 128.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -3.0); } void display(void) { static int start, end, last; start = glutGet(GLUT_ELAPSED_TIME); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (performance) { glColor3f(1.0, 1.0, 1.0); text(5, 5, 20, "%.2f fps", 1.0 / ((end - last) / 1000.0)); last = start; } glPushMatrix(); glTranslatef(pan_x, pan_y, 0.0); tbMatrix(); glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); glColor3f(0.5, 0.5, 0.5); glCallList(model_list); if (bounding_box) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glColor4f(1.0, 0.0, 0.0, 0.25); glutSolidCube(2.0); } glPopMatrix(); if (stats) { glColor3f(1.0, 1.0, 1.0); text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*1), 20, "%s", model->pathname); text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*2), 20, "%d vertices", model->numvertices); text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*3), 20, "%d triangles", model->numtriangles); text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*4), 20, "%d normals", model->numnormals); text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*5), 20, "%d texcoords", model->numtexcoords); text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*6), 20, "%d groups", model->numgroups); text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*7), 20, "%d materials", model->nummaterials); } glutSwapBuffers(); end = glutGet(GLUT_ELAPSED_TIME); } /* ARGSUSED1 */ void keyboard(unsigned char key, int x, int y) { GLint params[2]; switch (key) { case 'h': printf("help\n\n"); printf("w - Toggle wireframe/filled\n"); printf("c - Toggle culling\n"); printf("n - Toggle facet/smooth normal\n"); printf("b - Toggle bounding box\n"); printf("r - Reverse polygon winding\n"); printf("m - Toggle color/material/none mode\n"); printf("p - Toggle performance indicator\n"); printf("s/S - Scale model smaller/larger\n"); printf("t - Show model stats\n"); printf("o - Weld vertices in model\n"); printf("+/- - Increase/decrease smoothing angle\n"); printf("W - Write model to file (out.obj)\n"); printf("q/escape - Quit\n\n"); break; case 't': stats = !stats; break; case 'p': performance = !performance; break; case 'm': material_mode++; if (material_mode > 2) material_mode = 0; printf("material_mode = %d\n", material_mode); lists(); break; case 'd': glmDelete(model); init(); lists(); break; case 'w': glGetIntegerv(GL_POLYGON_MODE, params); if (params[0] == GL_FILL) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case 'c': if (glIsEnabled(GL_CULL_FACE)) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); break; case 'b': bounding_box = !bounding_box; break; case 'n': facet_normal = !facet_normal; lists(); break; case 'r': glmReverseWinding(model); lists(); break; case 's': glmScale(model, 0.8); lists(); break; case 'S': glmScale(model, 1.25); lists(); break; case 'o': glmWeld(model, weld_distance); glmVertexNormals(model, smoothing_angle); lists(); break; case 'O': weld_distance += 0.01; printf("Weld distance: %.2f\n", weld_distance); glmWeld(model, weld_distance); glmFacetNormals(model); glmVertexNormals(model, smoothing_angle); lists(); break; case '-': smoothing_angle -= 1.0; printf("Smoothing angle: %.1f\n", smoothing_angle); glmVertexNormals(model, smoothing_angle); lists(); break; case '+': smoothing_angle += 1.0; printf("Smoothing angle: %.1f\n", smoothing_angle); glmVertexNormals(model, smoothing_angle); lists(); break; case 'W': glmScale(model, 1.0/scale); glmWriteOBJ(model, "out.obj", GLM_SMOOTH | GLM_MATERIAL); break; case 'R': { GLuint i; GLfloat swap; for (i = 1; i <= model->numvertices; i++) { swap = model->vertices[3 * i + 1]; model->vertices[3 * i + 1] = model->vertices[3 * i + 2]; model->vertices[3 * i + 2] = -swap; } glmFacetNormals(model); lists(); break; } case 'q': case 27: exit(0); break; } glutPostRedisplay(); } void menu(int item) { keyboard((unsigned char)item, 0, 0); } void mouse(int button, int state, int x, int y) { GLdouble model[4*4]; GLdouble proj[4*4]; GLint view[4]; tbMouse(button, state, x, y); mouse_state = state; mouse_button = button; if (state == GLUT_DOWN && button == GLUT_LEFT_BUTTON) { glGetDoublev(GL_MODELVIEW_MATRIX, model); glGetDoublev(GL_PROJECTION_MATRIX, proj); glGetIntegerv(GL_VIEWPORT, view); gluProject((GLdouble)x, (GLdouble)y, 0.0, model, proj, view, &pan_x, &pan_y, &pan_z); gluUnProject((GLdouble)x, (GLdouble)y, pan_z, model, proj, view, &pan_x, &pan_y, &pan_z); pan_y = -pan_y; } glutPostRedisplay(); } void motion(int x, int y) { GLdouble model[4*4]; GLdouble proj[4*4]; GLint view[4]; tbMotion(x, y); if (mouse_state == GLUT_DOWN && mouse_button == GLUT_LEFT_BUTTON) { glGetDoublev(GL_MODELVIEW_MATRIX, model); glGetDoublev(GL_PROJECTION_MATRIX, proj); glGetIntegerv(GL_VIEWPORT, view); gluProject((GLdouble)x, (GLdouble)y, 0.0, model, proj, view, &pan_x, &pan_y, &pan_z); gluUnProject((GLdouble)x, (GLdouble)y, pan_z, model, proj, view, &pan_x, &pan_y, &pan_z); pan_y = -pan_y; } glutPostRedisplay(); } int main(int argc, char** argv) { glutInitWindowSize(512, 512); glutInit(&argc, argv); model_file = argv[1]; if (!model_file) { fprintf(stderr, "usage: smooth model_file.obj\n"); exit(1); } glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutCreateWindow("smooth"); glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMotionFunc(motion); glutCreateMenu(menu); glutAddMenuEntry("Smooth", 0); glutAddMenuEntry("", 0); glutAddMenuEntry("[w] Toggle wireframe/filled", 'w'); glutAddMenuEntry("[c] Toggle culling on/off", 'c'); glutAddMenuEntry("[n] Toggle facet/smooth normals", 'n'); glutAddMenuEntry("[b] Toggle bounding box on/off", 'b'); glutAddMenuEntry("[r] Reverse polygon winding", 'r'); glutAddMenuEntry("[m] Toggle color/material/none mode", 'm'); glutAddMenuEntry("[s] Scale model smaller", 's'); glutAddMenuEntry("[S] Scale model larger", 'S'); glutAddMenuEntry("[p] Toggle performance indicator", 'c'); glutAddMenuEntry("[o] Weld redundant vertices", 'c'); glutAddMenuEntry("[t] Show model stats", 'c'); glutAddMenuEntry("[+] Increase smoothing angle", '+'); glutAddMenuEntry("[-] Decrease smoothing angle", '-'); glutAddMenuEntry("[W] Write model to file (out.obj)", 'W'); glutAddMenuEntry("", 0); glutAddMenuEntry("[q] Quit", 27); glutAttachMenu(GLUT_RIGHT_BUTTON); init(); glutMainLoop(); return 0; }