Merge branch 'mesa_7_7_branch'
[mesa.git] / progs / objviewer / objview.c
1 /*
2 * .obj file viewer based on "smooth" by Nate Robins, 1997
3 *
4 * Brian Paul
5 * 1 Oct 2009
6 */
7
8 #include <math.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <stdarg.h>
13 #include <GL/glew.h>
14 #include <GL/glut.h>
15 #include "glm.h"
16 #include "readtex.h"
17 #include "skybox.h"
18 #include "trackball.h"
19
20
21 static char *Model_file = NULL; /* name of the obect file */
22 static GLMmodel *Model;
23 static GLfloat Scale = 4.0; /* scaling factor */
24 static GLboolean Performance = GL_FALSE;
25 static GLboolean Stats = GL_FALSE;
26 static GLboolean Animate = GL_TRUE;
27 static GLuint SkyboxTex;
28 static GLboolean Skybox = GL_TRUE;
29 static GLboolean Cull = GL_TRUE;
30 static GLboolean WireFrame = GL_FALSE;
31 static GLenum FrontFace = GL_CCW;
32 static GLfloat Yrot = 0.0;
33 static GLint WinWidth = 1024, WinHeight = 768;
34 static GLuint NumInstances = 1;
35
36
37
38 typedef struct
39 {
40 float CurQuat[4];
41 float Distance;
42 /* When mouse is moving: */
43 GLboolean Rotating, Translating;
44 GLint StartX, StartY;
45 float StartDistance;
46 } ViewInfo;
47
48 static ViewInfo View;
49
50 static void
51 InitViewInfo(ViewInfo *view)
52 {
53 view->Rotating = GL_FALSE;
54 view->Translating = GL_FALSE;
55 view->StartX = view->StartY = 0;
56 view->Distance = 12.0;
57 view->StartDistance = 0.0;
58 view->CurQuat[0] = 0.0;
59 view->CurQuat[1] = 1.0;
60 view->CurQuat[2] = 0.0;
61 view->CurQuat[3] = 0.0;
62 }
63
64
65
66 /* text: general purpose text routine. draws a string according to
67 * format in a stroke font at x, y after scaling it by the scale
68 * specified (scale is in window-space (lower-left origin) pixels).
69 *
70 * x - position in x (in window-space)
71 * y - position in y (in window-space)
72 * scale - scale in pixels
73 * format - as in printf()
74 */
75 static void
76 text(GLuint x, GLuint y, GLfloat scale, char* format, ...)
77 {
78 va_list args;
79 char buffer[255], *p;
80 GLfloat font_scale = 119.05 + 33.33;
81
82 va_start(args, format);
83 vsprintf(buffer, format, args);
84 va_end(args);
85
86 glMatrixMode(GL_PROJECTION);
87 glPushMatrix();
88 glLoadIdentity();
89 gluOrtho2D(0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT));
90
91 glMatrixMode(GL_MODELVIEW);
92 glPushMatrix();
93 glLoadIdentity();
94
95 glPushAttrib(GL_ENABLE_BIT);
96 glDisable(GL_LIGHTING);
97 glDisable(GL_TEXTURE_2D);
98 glDisable(GL_DEPTH_TEST);
99 glTranslatef(x, y, 0.0);
100
101 glScalef(scale/font_scale, scale/font_scale, scale/font_scale);
102
103 for(p = buffer; *p; p++)
104 glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
105
106 glPopAttrib();
107
108 glPopMatrix();
109 glMatrixMode(GL_PROJECTION);
110 glPopMatrix();
111 glMatrixMode(GL_MODELVIEW);
112 }
113
114
115 static float
116 ComputeFPS(void)
117 {
118 static double t0 = -1.0;
119 static int frames = 0;
120 double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
121 static float fps = 0;
122
123 frames++;
124
125 if (t0 < 0.0) {
126 t0 = t;
127 fps = 0.0;
128 }
129 else if (t - t0 >= 4.0) {
130 fps = (frames / (t - t0) + 0.5);
131 t0 = t;
132 frames = 0;
133 return fps;
134 }
135
136 return 0.0;
137 }
138
139
140 static void
141 init_model(void)
142 {
143 float objScale;
144
145 /* read in the model */
146 Model = glmReadOBJ(Model_file);
147 objScale = glmUnitize(Model);
148 glmFacetNormals(Model);
149 if (Model->numnormals == 0) {
150 GLfloat smoothing_angle = 90.0;
151 printf("Generating normals.\n");
152 glmVertexNormals(Model, smoothing_angle);
153 }
154
155 glmLoadTextures(Model);
156 glmReIndex(Model);
157 glmMakeVBOs(Model);
158 if (0)
159 glmPrint(Model);
160 }
161
162 static void
163 init_skybox(void)
164 {
165 SkyboxTex = LoadSkyBoxCubeTexture("alpine_east.rgb",
166 "alpine_west.rgb",
167 "alpine_up.rgb",
168 "alpine_down.rgb",
169 "alpine_south.rgb",
170 "alpine_north.rgb");
171 glmSpecularTexture(Model, SkyboxTex);
172 }
173
174
175 static void
176 init_gfx(void)
177 {
178 glEnable(GL_DEPTH_TEST);
179 glEnable(GL_CULL_FACE);
180 glEnable(GL_NORMALIZE);
181 glClearColor(0.3, 0.3, 0.9, 0.0);
182 }
183
184
185 static void
186 reshape(int width, int height)
187 {
188 float ar = 0.5 * (float) width / (float) height;
189
190 WinWidth = width;
191 WinHeight = height;
192
193 glViewport(0, 0, width, height);
194
195 glMatrixMode(GL_PROJECTION);
196 glLoadIdentity();
197 glFrustum(-ar, ar, -0.5, 0.5, 1.0, 300.0);
198 glMatrixMode(GL_MODELVIEW);
199 glLoadIdentity();
200 glTranslatef(0.0, 0.0, -3.0);
201 }
202
203
204 static void
205 Idle(void)
206 {
207 float q[4];
208 trackball(q, 100, 0, 99.99, 0);
209 add_quats(q, View.CurQuat, View.CurQuat);
210
211 glutPostRedisplay();
212 }
213
214
215 static void
216 display(void)
217 {
218 GLfloat rot[4][4];
219 float fps;
220
221 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
222
223 glPushMatrix();
224 glTranslatef(0.0, 0.0, -View.Distance);
225 glRotatef(Yrot, 0, 1, 0);
226 build_rotmatrix(rot, View.CurQuat);
227 glMultMatrixf(&rot[0][0]);
228 glScalef(Scale, Scale, Scale );
229
230 glUseProgram(0);
231
232 if (Skybox)
233 DrawSkyBoxCubeTexture(SkyboxTex);
234
235 if (WireFrame)
236 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
237 else
238 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
239
240 if (Cull)
241 glEnable(GL_CULL_FACE);
242 else
243 glDisable(GL_CULL_FACE);
244
245 if (NumInstances == 1) {
246 glmDrawVBO(Model);
247 }
248 else {
249 /* draw > 1 instance */
250 float dr = 360.0 / NumInstances;
251 float r;
252 for (r = 0.0; r < 360.0; r += dr) {
253 glPushMatrix();
254 glRotatef(r, 0, 1, 0);
255 glTranslatef(1.4, 0.0, 0.0);
256 glmDrawVBO(Model);
257 glPopMatrix();
258 }
259 }
260
261 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
262 glDisable(GL_CULL_FACE);
263
264 glPopMatrix();
265
266 if (Stats) {
267 glColor3f(1.0, 1.0, 1.0);
268 text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*1), 20, "%s",
269 Model->pathname);
270 text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*2), 20, "%d vertices",
271 Model->numvertices);
272 text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*3), 20, "%d triangles",
273 Model->numtriangles);
274 text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*4), 20, "%d normals",
275 Model->numnormals);
276 text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*5), 20, "%d texcoords",
277 Model->numtexcoords);
278 text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*6), 20, "%d groups",
279 Model->numgroups);
280 text(5, glutGet(GLUT_WINDOW_HEIGHT) - (5+20*7), 20, "%d materials",
281 Model->nummaterials);
282 }
283
284 glutSwapBuffers();
285
286 fps = ComputeFPS();
287 if (fps)
288 printf("%f FPS\n", fps);
289 }
290
291
292 static void
293 keyboard(unsigned char key, int x, int y)
294 {
295 switch (key) {
296 case 'h':
297 printf("help\n\n");
298 printf("a - Toggle animation\n");
299 printf("d/D - Decrease/Incrase number of models\n");
300 printf("w - Toggle wireframe/filled\n");
301 printf("c - Toggle culling\n");
302 printf("n - Toggle facet/smooth normal\n");
303 printf("r - Reverse polygon winding\n");
304 printf("p - Toggle performance indicator\n");
305 printf("s - Toggle skybox\n");
306 printf("z/Z - Scale model smaller/larger\n");
307 printf("i - Show model info/stats\n");
308 printf("q/escape - Quit\n\n");
309 break;
310 case 'a':
311 Animate = !Animate;
312 if (Animate)
313 glutIdleFunc(Idle);
314 else
315 glutIdleFunc(NULL);
316 break;
317 case 'd':
318 if (NumInstances > 1)
319 NumInstances--;
320 break;
321 case 'D':
322 NumInstances++;
323 break;
324 case 'i':
325 Stats = !Stats;
326 break;
327 case 'p':
328 Performance = !Performance;
329 break;
330 case 'w':
331 WireFrame = !WireFrame;
332 break;
333 case 'c':
334 Cull = !Cull;
335 printf("Polygon culling: %d\n", Cull);
336 break;
337 case 'r':
338 if (FrontFace == GL_CCW)
339 FrontFace = GL_CW;
340 else
341 FrontFace = GL_CCW;
342 glFrontFace(FrontFace);
343 printf("Front face:: %s\n", FrontFace == GL_CCW ? "CCW" : "CW");
344 break;
345 case 's':
346 Skybox = !Skybox;
347 if (Skybox)
348 glmSpecularTexture(Model, SkyboxTex);
349 else
350 glmSpecularTexture(Model, 0);
351 break;
352 case 'z':
353 Scale *= 0.9;
354 break;
355 case 'Z':
356 Scale *= 1.1;
357 break;
358 case 'q':
359 case 27:
360 exit(0);
361 break;
362 }
363
364 glutPostRedisplay();
365 }
366
367
368 static void
369 menu(int item)
370 {
371 keyboard((unsigned char)item, 0, 0);
372 }
373
374
375 /**
376 * Handle mouse button.
377 */
378 static void
379 Mouse(int button, int state, int x, int y)
380 {
381 if (button == GLUT_LEFT_BUTTON) {
382 if (state == GLUT_DOWN) {
383 View.StartX = x;
384 View.StartY = y;
385 View.Rotating = GL_TRUE;
386 }
387 else if (state == GLUT_UP) {
388 View.Rotating = GL_FALSE;
389 }
390 }
391 else if (button == GLUT_MIDDLE_BUTTON) {
392 if (state == GLUT_DOWN) {
393 View.StartX = x;
394 View.StartY = y;
395 View.StartDistance = View.Distance;
396 View.Translating = GL_TRUE;
397 }
398 else if (state == GLUT_UP) {
399 View.Translating = GL_FALSE;
400 }
401 }
402 }
403
404
405 /**
406 * Handle mouse motion
407 */
408 static void
409 Motion(int x, int y)
410 {
411 int i;
412 if (View.Rotating) {
413 float x0 = (2.0 * View.StartX - WinWidth) / WinWidth;
414 float y0 = (WinHeight - 2.0 * View.StartY) / WinHeight;
415 float x1 = (2.0 * x - WinWidth) / WinWidth;
416 float y1 = (WinHeight - 2.0 * y) / WinHeight;
417 float q[4];
418
419 trackball(q, x0, y0, x1, y1);
420 View.StartX = x;
421 View.StartY = y;
422 for (i = 0; i < 1; i++)
423 add_quats(q, View.CurQuat, View.CurQuat);
424
425 glutPostRedisplay();
426 }
427 else if (View.Translating) {
428 float dz = 0.02 * (y - View.StartY);
429 View.Distance = View.StartDistance + dz;
430 glutPostRedisplay();
431 }
432 }
433
434
435 static void
436 DoFeatureChecks(void)
437 {
438 char *version = (char *) glGetString(GL_VERSION);
439 if (version[0] == '1') {
440 /* check for individual extensions */
441 if (!glutExtensionSupported("GL_ARB_texture_cube_map")) {
442 printf("Sorry, GL_ARB_texture_cube_map is required.\n");
443 exit(1);
444 }
445 if (!glutExtensionSupported("GL_ARB_vertex_shader")) {
446 printf("Sorry, GL_ARB_vertex_shader is required.\n");
447 exit(1);
448 }
449 if (!glutExtensionSupported("GL_ARB_fragment_shader")) {
450 printf("Sorry, GL_ARB_fragment_shader is required.\n");
451 exit(1);
452 }
453 if (!glutExtensionSupported("GL_ARB_vertex_buffer_object")) {
454 printf("Sorry, GL_ARB_vertex_buffer_object is required.\n");
455 exit(1);
456 }
457 }
458 }
459
460
461 int
462 main(int argc, char** argv)
463 {
464 glutInitWindowSize(WinWidth, WinHeight);
465 glutInit(&argc, argv);
466
467 if (argc > 1) {
468 Model_file = argv[1];
469 }
470 if (!Model_file) {
471 fprintf(stderr, "usage: objview file.obj\n");
472 fprintf(stderr, "(using default bunny.obj)\n");
473 Model_file = "bunny.obj";
474 }
475
476 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
477 glutCreateWindow("objview");
478
479 glewInit();
480
481 DoFeatureChecks();
482
483 glutReshapeFunc(reshape);
484 glutDisplayFunc(display);
485 glutKeyboardFunc(keyboard);
486 glutMouseFunc(Mouse);
487 glutMotionFunc(Motion);
488 if (Animate)
489 glutIdleFunc(Idle);
490
491 glutCreateMenu(menu);
492 glutAddMenuEntry("[a] Toggle animate", 'a');
493 glutAddMenuEntry("[d] Fewer models", 'd');
494 glutAddMenuEntry("[D] More models", 'D');
495 glutAddMenuEntry("[w] Toggle wireframe/filled", 'w');
496 glutAddMenuEntry("[c] Toggle culling on/off", 'c');
497 glutAddMenuEntry("[r] Reverse polygon winding", 'r');
498 glutAddMenuEntry("[z] Scale model smaller", 'z');
499 glutAddMenuEntry("[Z] Scale model larger", 'Z');
500 glutAddMenuEntry("[p] Toggle performance indicator", 'p');
501 glutAddMenuEntry("[i] Show model stats", 'i');
502 glutAddMenuEntry("", 0);
503 glutAddMenuEntry("[q] Quit", 27);
504 glutAttachMenu(GLUT_RIGHT_BUTTON);
505
506 InitViewInfo(&View);
507
508 init_model();
509 init_skybox();
510 init_gfx();
511
512 glutMainLoop();
513
514 return 0;
515 }