diff options
Diffstat (limited to 'progs/objviewer/objview.c')
-rw-r--r-- | progs/objviewer/objview.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/progs/objviewer/objview.c b/progs/objviewer/objview.c new file mode 100644 index 0000000000..ad25e751a7 --- /dev/null +++ b/progs/objviewer/objview.c @@ -0,0 +1,515 @@ +/* + * .obj file viewer based on "smooth" by Nate Robins, 1997 + * + * Brian Paul + * 1 Oct 2009 + */ + +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <stdarg.h> +#include <GL/glew.h> +#include <GL/glut.h> +#include "glm.h" +#include "readtex.h" +#include "skybox.h" +#include "trackball.h" + + +static char *Model_file = NULL; /* name of the obect file */ +static GLMmodel *Model; +static GLfloat Scale = 4.0; /* scaling factor */ +static GLboolean Performance = GL_FALSE; +static GLboolean Stats = GL_FALSE; +static GLboolean Animate = GL_TRUE; +static GLuint SkyboxTex; +static GLboolean Skybox = GL_TRUE; +static GLboolean Cull = GL_TRUE; +static GLboolean WireFrame = GL_FALSE; +static GLenum FrontFace = GL_CCW; +static GLfloat Yrot = 0.0; +static GLint WinWidth = 1024, WinHeight = 768; +static GLuint NumInstances = 1; + + + +typedef struct +{ + float CurQuat[4]; + float Distance; + /* When mouse is moving: */ + GLboolean Rotating, Translating; + GLint StartX, StartY; + float StartDistance; +} ViewInfo; + +static ViewInfo View; + +static void +InitViewInfo(ViewInfo *view) +{ + view->Rotating = GL_FALSE; + view->Translating = GL_FALSE; + view->StartX = view->StartY = 0; + view->Distance = 12.0; + view->StartDistance = 0.0; + view->CurQuat[0] = 0.0; + view->CurQuat[1] = 1.0; + view->CurQuat[2] = 0.0; + view->CurQuat[3] = 0.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() + */ +static 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); +} + + +static float +ComputeFPS(void) +{ + static double t0 = -1.0; + static int frames = 0; + double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; + static float fps = 0; + + frames++; + + if (t0 < 0.0) { + t0 = t; + fps = 0.0; + } + else if (t - t0 >= 4.0) { + fps = (frames / (t - t0) + 0.5); + t0 = t; + frames = 0; + return fps; + } + + return 0.0; +} + + +static void +init_model(void) +{ + float objScale; + + /* read in the model */ + Model = glmReadOBJ(Model_file); + objScale = glmUnitize(Model); + glmFacetNormals(Model); + if (Model->numnormals == 0) { + GLfloat smoothing_angle = 90.0; + printf("Generating normals.\n"); + glmVertexNormals(Model, smoothing_angle); + } + + glmLoadTextures(Model); + glmReIndex(Model); + glmMakeVBOs(Model); + if (0) + glmPrint(Model); +} + +static void +init_skybox(void) +{ + SkyboxTex = LoadSkyBoxCubeTexture("alpine_east.rgb", + "alpine_west.rgb", + "alpine_up.rgb", + "alpine_down.rgb", + "alpine_south.rgb", + "alpine_north.rgb"); + glmSpecularTexture(Model, SkyboxTex); +} + + +static void +init_gfx(void) +{ + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_NORMALIZE); + glClearColor(0.3, 0.3, 0.9, 0.0); +} + + +static void +reshape(int width, int height) +{ + float ar = 0.5 * (float) width / (float) height; + + WinWidth = width; + WinHeight = height; + + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-ar, ar, -0.5, 0.5, 1.0, 300.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0, 0.0, -3.0); +} + + +static void +Idle(void) +{ + float q[4]; + trackball(q, 100, 0, 99.99, 0); + add_quats(q, View.CurQuat, View.CurQuat); + + glutPostRedisplay(); +} + + +static void +display(void) +{ + GLfloat rot[4][4]; + float fps; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + glTranslatef(0.0, 0.0, -View.Distance); + glRotatef(Yrot, 0, 1, 0); + build_rotmatrix(rot, View.CurQuat); + glMultMatrixf(&rot[0][0]); + glScalef(Scale, Scale, Scale ); + + glUseProgram(0); + + if (Skybox) + DrawSkyBoxCubeTexture(SkyboxTex); + + if (WireFrame) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + else + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + if (Cull) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + if (NumInstances == 1) { + glmDrawVBO(Model); + } + else { + /* draw > 1 instance */ + float dr = 360.0 / NumInstances; + float r; + for (r = 0.0; r < 360.0; r += dr) { + glPushMatrix(); + glRotatef(r, 0, 1, 0); + glTranslatef(1.4, 0.0, 0.0); + glmDrawVBO(Model); + glPopMatrix(); + } + } + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_CULL_FACE); + + 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(); + + fps = ComputeFPS(); + if (fps) + printf("%f FPS\n", fps); +} + + +static void +keyboard(unsigned char key, int x, int y) +{ + switch (key) { + case 'h': + printf("help\n\n"); + printf("a - Toggle animation\n"); + printf("d/D - Decrease/Incrase number of models\n"); + printf("w - Toggle wireframe/filled\n"); + printf("c - Toggle culling\n"); + printf("n - Toggle facet/smooth normal\n"); + printf("r - Reverse polygon winding\n"); + printf("p - Toggle performance indicator\n"); + printf("s - Toggle skybox\n"); + printf("z/Z - Scale model smaller/larger\n"); + printf("i - Show model info/stats\n"); + printf("q/escape - Quit\n\n"); + break; + case 'a': + Animate = !Animate; + if (Animate) + glutIdleFunc(Idle); + else + glutIdleFunc(NULL); + break; + case 'd': + if (NumInstances > 1) + NumInstances--; + break; + case 'D': + NumInstances++; + break; + case 'i': + Stats = !Stats; + break; + case 'p': + Performance = !Performance; + break; + case 'w': + WireFrame = !WireFrame; + break; + case 'c': + Cull = !Cull; + printf("Polygon culling: %d\n", Cull); + break; + case 'r': + if (FrontFace == GL_CCW) + FrontFace = GL_CW; + else + FrontFace = GL_CCW; + glFrontFace(FrontFace); + printf("Front face:: %s\n", FrontFace == GL_CCW ? "CCW" : "CW"); + break; + case 's': + Skybox = !Skybox; + if (Skybox) + glmSpecularTexture(Model, SkyboxTex); + else + glmSpecularTexture(Model, 0); + break; + case 'z': + Scale *= 0.9; + break; + case 'Z': + Scale *= 1.1; + break; + case 'q': + case 27: + exit(0); + break; + } + + glutPostRedisplay(); +} + + +static void +menu(int item) +{ + keyboard((unsigned char)item, 0, 0); +} + + +/** + * Handle mouse button. + */ +static void +Mouse(int button, int state, int x, int y) +{ + if (button == GLUT_LEFT_BUTTON) { + if (state == GLUT_DOWN) { + View.StartX = x; + View.StartY = y; + View.Rotating = GL_TRUE; + } + else if (state == GLUT_UP) { + View.Rotating = GL_FALSE; + } + } + else if (button == GLUT_MIDDLE_BUTTON) { + if (state == GLUT_DOWN) { + View.StartX = x; + View.StartY = y; + View.StartDistance = View.Distance; + View.Translating = GL_TRUE; + } + else if (state == GLUT_UP) { + View.Translating = GL_FALSE; + } + } +} + + +/** + * Handle mouse motion + */ +static void +Motion(int x, int y) +{ + int i; + if (View.Rotating) { + float x0 = (2.0 * View.StartX - WinWidth) / WinWidth; + float y0 = (WinHeight - 2.0 * View.StartY) / WinHeight; + float x1 = (2.0 * x - WinWidth) / WinWidth; + float y1 = (WinHeight - 2.0 * y) / WinHeight; + float q[4]; + + trackball(q, x0, y0, x1, y1); + View.StartX = x; + View.StartY = y; + for (i = 0; i < 1; i++) + add_quats(q, View.CurQuat, View.CurQuat); + + glutPostRedisplay(); + } + else if (View.Translating) { + float dz = 0.02 * (y - View.StartY); + View.Distance = View.StartDistance + dz; + glutPostRedisplay(); + } +} + + +static void +DoFeatureChecks(void) +{ + char *version = (char *) glGetString(GL_VERSION); + if (version[0] == '1') { + /* check for individual extensions */ + if (!glutExtensionSupported("GL_ARB_texture_cube_map")) { + printf("Sorry, GL_ARB_texture_cube_map is required.\n"); + exit(1); + } + if (!glutExtensionSupported("GL_ARB_vertex_shader")) { + printf("Sorry, GL_ARB_vertex_shader is required.\n"); + exit(1); + } + if (!glutExtensionSupported("GL_ARB_fragment_shader")) { + printf("Sorry, GL_ARB_fragment_shader is required.\n"); + exit(1); + } + if (!glutExtensionSupported("GL_ARB_vertex_buffer_object")) { + printf("Sorry, GL_ARB_vertex_buffer_object is required.\n"); + exit(1); + } + } +} + + +int +main(int argc, char** argv) +{ + glutInitWindowSize(WinWidth, WinHeight); + glutInit(&argc, argv); + + if (argc > 1) { + Model_file = argv[1]; + } + if (!Model_file) { + fprintf(stderr, "usage: objview file.obj\n"); + fprintf(stderr, "(using default bunny.obj)\n"); + Model_file = "bunny.obj"; + } + + glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); + glutCreateWindow("objview"); + + glewInit(); + + DoFeatureChecks(); + + glutReshapeFunc(reshape); + glutDisplayFunc(display); + glutKeyboardFunc(keyboard); + glutMouseFunc(Mouse); + glutMotionFunc(Motion); + if (Animate) + glutIdleFunc(Idle); + + glutCreateMenu(menu); + glutAddMenuEntry("[a] Toggle animate", 'a'); + glutAddMenuEntry("[d] Fewer models", 'd'); + glutAddMenuEntry("[D] More models", 'D'); + glutAddMenuEntry("[w] Toggle wireframe/filled", 'w'); + glutAddMenuEntry("[c] Toggle culling on/off", 'c'); + glutAddMenuEntry("[r] Reverse polygon winding", 'r'); + glutAddMenuEntry("[z] Scale model smaller", 'z'); + glutAddMenuEntry("[Z] Scale model larger", 'Z'); + glutAddMenuEntry("[p] Toggle performance indicator", 'p'); + glutAddMenuEntry("[i] Show model stats", 'i'); + glutAddMenuEntry("", 0); + glutAddMenuEntry("[q] Quit", 27); + glutAttachMenu(GLUT_RIGHT_BUTTON); + + InitViewInfo(&View); + + init_model(); + init_skybox(); + init_gfx(); + + glutMainLoop(); + + return 0; +} |