2 * .obj file viewer based on "smooth" by Nate Robins, 1997
18 #include "trackball.h"
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;
42 /* When mouse is moving: */
43 GLboolean Rotating
, Translating
;
51 InitViewInfo(ViewInfo
*view
)
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;
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).
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()
76 text(GLuint x
, GLuint y
, GLfloat scale
, char* format
, ...)
80 GLfloat font_scale
= 119.05 + 33.33;
82 va_start(args
, format
);
83 vsprintf(buffer
, format
, args
);
86 glMatrixMode(GL_PROJECTION
);
89 gluOrtho2D(0, glutGet(GLUT_WINDOW_WIDTH
), 0, glutGet(GLUT_WINDOW_HEIGHT
));
91 glMatrixMode(GL_MODELVIEW
);
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);
101 glScalef(scale
/font_scale
, scale
/font_scale
, scale
/font_scale
);
103 for(p
= buffer
; *p
; p
++)
104 glutStrokeCharacter(GLUT_STROKE_ROMAN
, *p
);
109 glMatrixMode(GL_PROJECTION
);
111 glMatrixMode(GL_MODELVIEW
);
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;
129 else if (t
- t0
>= 4.0) {
130 fps
= (frames
/ (t
- t0
) + 0.5);
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
);
155 glmLoadTextures(Model
);
165 SkyboxTex
= LoadSkyBoxCubeTexture("alpine_east.rgb",
171 glmSpecularTexture(Model
, SkyboxTex
);
178 glEnable(GL_DEPTH_TEST
);
179 glEnable(GL_CULL_FACE
);
180 glEnable(GL_NORMALIZE
);
181 glClearColor(0.3, 0.3, 0.9, 0.0);
186 reshape(int width
, int height
)
188 float ar
= 0.5 * (float) width
/ (float) height
;
193 glViewport(0, 0, width
, height
);
195 glMatrixMode(GL_PROJECTION
);
197 glFrustum(-ar
, ar
, -0.5, 0.5, 1.0, 300.0);
198 glMatrixMode(GL_MODELVIEW
);
200 glTranslatef(0.0, 0.0, -3.0);
208 trackball(q
, 100, 0, 99.99, 0);
209 add_quats(q
, View
.CurQuat
, View
.CurQuat
);
221 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
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
);
233 DrawSkyBoxCubeTexture(SkyboxTex
);
236 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
238 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
241 glEnable(GL_CULL_FACE
);
243 glDisable(GL_CULL_FACE
);
245 if (NumInstances
== 1) {
249 /* draw > 1 instance */
250 float dr
= 360.0 / NumInstances
;
252 for (r
= 0.0; r
< 360.0; r
+= dr
) {
254 glRotatef(r
, 0, 1, 0);
255 glTranslatef(1.4, 0.0, 0.0);
261 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
262 glDisable(GL_CULL_FACE
);
267 glColor3f(1.0, 1.0, 1.0);
268 text(5, glutGet(GLUT_WINDOW_HEIGHT
) - (5+20*1), 20, "%s",
270 text(5, glutGet(GLUT_WINDOW_HEIGHT
) - (5+20*2), 20, "%d vertices",
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",
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",
280 text(5, glutGet(GLUT_WINDOW_HEIGHT
) - (5+20*7), 20, "%d materials",
281 Model
->nummaterials
);
288 printf("%f FPS\n", fps
);
293 keyboard(unsigned char key
, int x
, int y
)
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");
318 if (NumInstances
> 1)
328 Performance
= !Performance
;
331 WireFrame
= !WireFrame
;
335 printf("Polygon culling: %d\n", Cull
);
338 if (FrontFace
== GL_CCW
)
342 glFrontFace(FrontFace
);
343 printf("Front face:: %s\n", FrontFace
== GL_CCW
? "CCW" : "CW");
348 glmSpecularTexture(Model
, SkyboxTex
);
350 glmSpecularTexture(Model
, 0);
371 keyboard((unsigned char)item
, 0, 0);
376 * Handle mouse button.
379 Mouse(int button
, int state
, int x
, int y
)
381 if (button
== GLUT_LEFT_BUTTON
) {
382 if (state
== GLUT_DOWN
) {
385 View
.Rotating
= GL_TRUE
;
387 else if (state
== GLUT_UP
) {
388 View
.Rotating
= GL_FALSE
;
391 else if (button
== GLUT_MIDDLE_BUTTON
) {
392 if (state
== GLUT_DOWN
) {
395 View
.StartDistance
= View
.Distance
;
396 View
.Translating
= GL_TRUE
;
398 else if (state
== GLUT_UP
) {
399 View
.Translating
= GL_FALSE
;
406 * Handle mouse motion
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
;
419 trackball(q
, x0
, y0
, x1
, y1
);
422 for (i
= 0; i
< 1; i
++)
423 add_quats(q
, View
.CurQuat
, View
.CurQuat
);
427 else if (View
.Translating
) {
428 float dz
= 0.02 * (y
- View
.StartY
);
429 View
.Distance
= View
.StartDistance
+ dz
;
436 DoFeatureChecks(void)
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");
445 if (!glutExtensionSupported("GL_ARB_vertex_shader")) {
446 printf("Sorry, GL_ARB_vertex_shader is required.\n");
449 if (!glutExtensionSupported("GL_ARB_fragment_shader")) {
450 printf("Sorry, GL_ARB_fragment_shader is required.\n");
453 if (!glutExtensionSupported("GL_ARB_vertex_buffer_object")) {
454 printf("Sorry, GL_ARB_vertex_buffer_object is required.\n");
462 main(int argc
, char** argv
)
464 glutInitWindowSize(WinWidth
, WinHeight
);
465 glutInit(&argc
, argv
);
468 Model_file
= argv
[1];
471 fprintf(stderr
, "usage: objview file.obj\n");
472 fprintf(stderr
, "(using default bunny.obj)\n");
473 Model_file
= "bunny.obj";
476 glutInitDisplayMode(GLUT_RGB
| GLUT_DEPTH
| GLUT_DOUBLE
);
477 glutCreateWindow("objview");
483 glutReshapeFunc(reshape
);
484 glutDisplayFunc(display
);
485 glutKeyboardFunc(keyboard
);
486 glutMouseFunc(Mouse
);
487 glutMotionFunc(Motion
);
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
);