mesa/progs/objviewer/objview.c

516 lines
12 KiB
C

/*
* .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;
}