summaryrefslogtreecommitdiff
path: root/progs/objviewer/objview.c
diff options
context:
space:
mode:
Diffstat (limited to 'progs/objviewer/objview.c')
-rw-r--r--progs/objviewer/objview.c515
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;
+}