progs/demos: add fps output to cubemap
[mesa.git] / progs / demos / cubemap.c
1 /*
2 * GL_ARB_texture_cube_map demo
3 *
4 * Brian Paul
5 * May 2000
6 *
7 *
8 * Copyright (C) 2000 Brian Paul All Rights Reserved.
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
24 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28
29 /*
30 * This is a pretty minimalistic demo for now. Eventually, use some
31 * interesting cube map textures and 3D objects.
32 * For now, we use 6 checkerboard "walls" and a sphere (good for
33 * verification purposes).
34 */
35
36
37 #include <assert.h>
38 #include <math.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <GL/glew.h>
43 #include "GL/glut.h"
44 #include "readtex.h"
45
46
47 static GLfloat Xrot = 0, Yrot = 0;
48 static GLfloat EyeDist = 10;
49 static GLboolean use_vertex_arrays = GL_FALSE;
50 static GLboolean anim = GL_TRUE;
51 static GLboolean NoClear = GL_FALSE;
52 static GLint FrameParity = 0;
53 static GLenum FilterIndex = 0;
54 static GLint ClampIndex = 0;
55 static GLboolean supportFBO = GL_FALSE;
56 static GLint T0 = 0;
57 static GLint Frames = 0;
58
59
60 static struct {
61 GLenum mode;
62 const char *name;
63 } ClampModes[] = {
64 { GL_CLAMP_TO_EDGE, "GL_CLAMP_TO_EDGE" },
65 { GL_CLAMP_TO_BORDER, "GL_CLAMP_TO_BORDER" },
66 { GL_CLAMP, "GL_CLAMP" },
67 { GL_REPEAT, "GL_REPEAT" }
68 };
69
70 #define NUM_CLAMP_MODES (sizeof(ClampModes) / sizeof(ClampModes[0]))
71
72
73 static struct {
74 GLenum mag_mode, min_mode;
75 const char *name;
76 } FilterModes[] = {
77 { GL_NEAREST, GL_NEAREST, "GL_NEAREST, GL_NEAREST" },
78 { GL_NEAREST, GL_LINEAR, "GL_NEAREST, GL_LINEAR" },
79 { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, "GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST" },
80 { GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR, "GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR" },
81 { GL_NEAREST, GL_LINEAR_MIPMAP_NEAREST, "GL_NEAREST, GL_LINEAR_MIPMAP_NEAREST" },
82 { GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR, "GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR" },
83
84 { GL_LINEAR, GL_NEAREST, "GL_LINEAR, GL_NEAREST" },
85 { GL_LINEAR, GL_LINEAR, "GL_LINEAR, GL_LINEAR" },
86 { GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, "GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST" },
87 { GL_LINEAR, GL_NEAREST_MIPMAP_LINEAR, "GL_LINEAR, GL_NEAREST_MIPMAP_LINEAR" },
88 { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, "GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST" },
89 { GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, "GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR" }
90 };
91
92 #define NUM_FILTER_MODES (sizeof(FilterModes) / sizeof(FilterModes[0]))
93
94
95
96 #define eps1 0.99
97 #define br 20.0 /* box radius */
98
99 static const GLfloat tex_coords[] = {
100 /* +X side */
101 1.0, -eps1, -eps1,
102 1.0, -eps1, eps1,
103 1.0, eps1, eps1,
104 1.0, eps1, -eps1,
105
106 /* -X side */
107 -1.0, eps1, -eps1,
108 -1.0, eps1, eps1,
109 -1.0, -eps1, eps1,
110 -1.0, -eps1, -eps1,
111
112 /* +Y side */
113 -eps1, 1.0, -eps1,
114 -eps1, 1.0, eps1,
115 eps1, 1.0, eps1,
116 eps1, 1.0, -eps1,
117
118 /* -Y side */
119 -eps1, -1.0, -eps1,
120 -eps1, -1.0, eps1,
121 eps1, -1.0, eps1,
122 eps1, -1.0, -eps1,
123
124 /* +Z side */
125 eps1, -eps1, 1.0,
126 -eps1, -eps1, 1.0,
127 -eps1, eps1, 1.0,
128 eps1, eps1, 1.0,
129
130 /* -Z side */
131 eps1, eps1, -1.0,
132 -eps1, eps1, -1.0,
133 -eps1, -eps1, -1.0,
134 eps1, -eps1, -1.0,
135 };
136
137 static const GLfloat vtx_coords[] = {
138 /* +X side */
139 br, -br, -br,
140 br, -br, br,
141 br, br, br,
142 br, br, -br,
143
144 /* -X side */
145 -br, br, -br,
146 -br, br, br,
147 -br, -br, br,
148 -br, -br, -br,
149
150 /* +Y side */
151 -br, br, -br,
152 -br, br, br,
153 br, br, br,
154 br, br, -br,
155
156 /* -Y side */
157 -br, -br, -br,
158 -br, -br, br,
159 br, -br, br,
160 br, -br, -br,
161
162 /* +Z side */
163 br, -br, br,
164 -br, -br, br,
165 -br, br, br,
166 br, br, br,
167
168 /* -Z side */
169 br, br, -br,
170 -br, br, -br,
171 -br, -br, -br,
172 br, -br, -br,
173 };
174
175 static void draw_skybox( void )
176 {
177 if ( use_vertex_arrays ) {
178 glTexCoordPointer( 3, GL_FLOAT, 0, tex_coords );
179 glVertexPointer( 3, GL_FLOAT, 0, vtx_coords );
180
181 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
182 glEnableClientState( GL_VERTEX_ARRAY );
183
184 glDrawArrays( GL_QUADS, 0, 24 );
185
186 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
187 glDisableClientState( GL_VERTEX_ARRAY );
188 }
189 else {
190 unsigned i;
191
192 glBegin(GL_QUADS);
193 for ( i = 0 ; i < 24 ; i++ ) {
194 glTexCoord3fv( & tex_coords[ i * 3 ] );
195 glVertex3fv ( & vtx_coords[ i * 3 ] );
196 }
197 glEnd();
198 }
199 }
200
201
202 static void draw( void )
203 {
204 GLenum wrap;
205
206 if (NoClear) {
207 /* This demonstrates how we can avoid calling glClear.
208 * This method only works if every pixel in the window is painted for
209 * every frame.
210 * We can simply skip clearing of the color buffer in this case.
211 * For the depth buffer, we alternately use a different subrange of
212 * the depth buffer for each frame. For the odd frame use the range
213 * [0, 0.5] with GL_LESS. For the even frames, use the range [1, 0.5]
214 * with GL_GREATER.
215 */
216 FrameParity = 1 - FrameParity;
217 if (FrameParity) {
218 glDepthRange(0.0, 0.5);
219 glDepthFunc(GL_LESS);
220 }
221 else {
222 glDepthRange(1.0, 0.5);
223 glDepthFunc(GL_GREATER);
224 }
225 }
226 else {
227 /* ordinary clearing */
228 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
229 }
230
231 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER,
232 FilterModes[FilterIndex].min_mode);
233 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER,
234 FilterModes[FilterIndex].mag_mode);
235
236 wrap = ClampModes[ClampIndex].mode;
237 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, wrap);
238 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, wrap);
239 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, wrap);
240
241 glPushMatrix(); /*MODELVIEW*/
242 glTranslatef( 0.0, 0.0, -EyeDist );
243
244 /* skybox */
245 glDisable(GL_TEXTURE_GEN_S);
246 glDisable(GL_TEXTURE_GEN_T);
247 glDisable(GL_TEXTURE_GEN_R);
248
249 glMatrixMode(GL_MODELVIEW);
250 glPushMatrix();
251 glRotatef(Xrot, 1, 0, 0);
252 glRotatef(Yrot, 0, 1, 0);
253 draw_skybox();
254 glPopMatrix();
255
256 /* sphere */
257 glMatrixMode(GL_TEXTURE);
258 glLoadIdentity();
259 glRotatef(-Yrot, 0, 1, 0);
260 glRotatef(-Xrot, 1, 0, 0);
261
262 glEnable(GL_TEXTURE_GEN_S);
263 glEnable(GL_TEXTURE_GEN_T);
264 glEnable(GL_TEXTURE_GEN_R);
265 glutSolidSphere(2.0, 20, 20);
266
267 glLoadIdentity(); /* texture */
268
269 glMatrixMode(GL_MODELVIEW);
270 glPopMatrix();
271
272 glutSwapBuffers();
273
274 Frames++;
275
276 {
277 GLint t = glutGet(GLUT_ELAPSED_TIME);
278 if (t - T0 >= 5000) {
279 GLfloat seconds = (t - T0) / 1000.0;
280 GLfloat fps = Frames / seconds;
281 printf("%d frames in %6.3f seconds = %6.3f FPS\n", Frames, seconds, fps);
282 fflush(stdout);
283 T0 = t;
284 Frames = 0;
285 }
286 }
287 }
288
289
290 static void idle(void)
291 {
292 GLfloat t = 0.05 * glutGet(GLUT_ELAPSED_TIME);
293 Yrot = t;
294 glutPostRedisplay();
295 }
296
297
298 static void set_mode(GLuint mode)
299 {
300 if (mode == 0) {
301 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
302 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
303 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
304 printf("GL_REFLECTION_MAP_ARB mode\n");
305 }
306 else if (mode == 1) {
307 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
308 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
309 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
310 printf("GL_NORMAL_MAP_ARB mode\n");
311 }
312 }
313
314
315 static void key(unsigned char k, int x, int y)
316 {
317 static GLuint mode = 0;
318 (void) x;
319 (void) y;
320 switch (k) {
321 case ' ':
322 anim = !anim;
323 if (anim)
324 glutIdleFunc(idle);
325 else
326 glutIdleFunc(NULL);
327 break;
328 case 'f':
329 FilterIndex = (FilterIndex + 1) % NUM_FILTER_MODES;
330 printf("Tex filter: %s\n", FilterModes[FilterIndex].name);
331 break;
332 case 'c':
333 ClampIndex = (ClampIndex + 1) % NUM_CLAMP_MODES;
334 printf("Tex wrap mode: %s\n", ClampModes[ClampIndex].name);
335 break;
336 case 'm':
337 mode = !mode;
338 set_mode(mode);
339 break;
340 case 'v':
341 use_vertex_arrays = ! use_vertex_arrays;
342 printf( "Vertex arrays are %sabled\n",
343 (use_vertex_arrays) ? "en" : "dis" );
344 break;
345 case 'z':
346 EyeDist -= 0.5;
347 if (EyeDist < 6.0)
348 EyeDist = 6.0;
349 break;
350 case 'Z':
351 EyeDist += 0.5;
352 if (EyeDist > 90.0)
353 EyeDist = 90;
354 break;
355 case 27:
356 exit(0);
357 }
358 glutPostRedisplay();
359 }
360
361
362 static void specialkey(int key, int x, int y)
363 {
364 GLfloat step = 5;
365 (void) x;
366 (void) y;
367 switch (key) {
368 case GLUT_KEY_UP:
369 Xrot += step;
370 break;
371 case GLUT_KEY_DOWN:
372 Xrot -= step;
373 break;
374 case GLUT_KEY_LEFT:
375 Yrot -= step;
376 break;
377 case GLUT_KEY_RIGHT:
378 Yrot += step;
379 break;
380 }
381 glutPostRedisplay();
382 }
383
384
385 /* new window size or exposure */
386 static void reshape(int width, int height)
387 {
388 GLfloat ar = (float) width / (float) height;
389 glViewport(0, 0, (GLint)width, (GLint)height);
390 glMatrixMode(GL_PROJECTION);
391 glLoadIdentity();
392 glFrustum( -2.0*ar, 2.0*ar, -2.0, 2.0, 4.0, 100.0 );
393 glMatrixMode(GL_MODELVIEW);
394 glLoadIdentity();
395 }
396
397
398 static void init_checkers( void )
399 {
400 #define CUBE_TEX_SIZE 64
401 GLubyte image[CUBE_TEX_SIZE][CUBE_TEX_SIZE][4];
402 static const GLubyte colors[6][3] = {
403 { 255, 0, 0 }, /* face 0 - red */
404 { 0, 255, 255 }, /* face 1 - cyan */
405 { 0, 255, 0 }, /* face 2 - green */
406 { 255, 0, 255 }, /* face 3 - purple */
407 { 0, 0, 255 }, /* face 4 - blue */
408 { 255, 255, 0 } /* face 5 - yellow */
409 };
410 static const GLenum targets[6] = {
411 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
412 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
413 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
414 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
415 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
416 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
417 };
418
419 GLint i, j, f;
420
421 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
422
423 if (!supportFBO)
424 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
425
426
427 /* make colored checkerboard cube faces */
428 for (f = 0; f < 6; f++) {
429 for (i = 0; i < CUBE_TEX_SIZE; i++) {
430 for (j = 0; j < CUBE_TEX_SIZE; j++) {
431 if ((i/4 + j/4) & 1) {
432 image[i][j][0] = colors[f][2];
433 image[i][j][1] = colors[f][1];
434 image[i][j][2] = colors[f][0];
435 image[i][j][3] = 255;
436 }
437 else {
438 image[i][j][0] = 255;
439 image[i][j][1] = 255;
440 image[i][j][2] = 255;
441 image[i][j][3] = 255;
442 }
443 }
444 }
445
446 glTexImage2D(targets[f], 0, GL_RGBA8, CUBE_TEX_SIZE, CUBE_TEX_SIZE, 0,
447 GL_BGRA, GL_UNSIGNED_BYTE, image);
448 }
449
450 if (supportFBO)
451 glGenerateMipmapEXT(GL_TEXTURE_CUBE_MAP_ARB);
452 }
453
454
455 static void load(GLenum target, const char *filename,
456 GLboolean flipTB, GLboolean flipLR)
457 {
458 GLint w, h;
459 GLenum format;
460 GLubyte *img = LoadRGBImage( filename, &w, &h, &format );
461 if (!img) {
462 printf("Error: couldn't load texture image %s\n", filename);
463 exit(1);
464 }
465 assert(format == GL_RGB);
466
467 /* <sigh> the way the texture cube mapping works, we have to flip
468 * images to make things look right.
469 */
470 if (flipTB) {
471 const int stride = 3 * w;
472 GLubyte temp[3*1024];
473 int i;
474 for (i = 0; i < h / 2; i++) {
475 memcpy(temp, img + i * stride, stride);
476 memcpy(img + i * stride, img + (h - i - 1) * stride, stride);
477 memcpy(img + (h - i - 1) * stride, temp, stride);
478 }
479 }
480 if (flipLR) {
481 const int stride = 3 * w;
482 GLubyte temp[3];
483 GLubyte *row;
484 int i, j;
485 for (i = 0; i < h; i++) {
486 row = img + i * stride;
487 for (j = 0; j < w / 2; j++) {
488 int k = w - j - 1;
489 temp[0] = row[j*3+0];
490 temp[1] = row[j*3+1];
491 temp[2] = row[j*3+2];
492 row[j*3+0] = row[k*3+0];
493 row[j*3+1] = row[k*3+1];
494 row[j*3+2] = row[k*3+2];
495 row[k*3+0] = temp[0];
496 row[k*3+1] = temp[1];
497 row[k*3+2] = temp[2];
498 }
499 }
500 }
501
502 gluBuild2DMipmaps(target, GL_RGB, w, h, format, GL_UNSIGNED_BYTE, img);
503 free(img);
504 }
505
506
507 static void load_envmaps(void)
508 {
509 load(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, "right.rgb", GL_TRUE, GL_FALSE);
510 load(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, "left.rgb", GL_TRUE, GL_FALSE);
511 load(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, "top.rgb", GL_FALSE, GL_TRUE);
512 load(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, "bottom.rgb", GL_FALSE, GL_TRUE);
513 load(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, "front.rgb", GL_TRUE, GL_FALSE);
514 load(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, "back.rgb", GL_TRUE, GL_FALSE);
515 }
516
517
518 static void init( GLboolean useImageFiles )
519 {
520 /* check for extensions */
521 {
522 char *exten = (char *) glGetString(GL_EXTENSIONS);
523 if (!strstr(exten, "GL_ARB_texture_cube_map")) {
524 printf("Sorry, this demo requires GL_ARB_texture_cube_map\n");
525 exit(0);
526 }
527
528 /* Needed for glGenerateMipmapEXT / auto mipmapping
529 */
530 if (strstr(exten, "GL_EXT_framebuffer_object")) {
531 supportFBO = GL_TRUE;
532 }
533 else if (!strstr(exten, "GL_SGIS_generate_mipmap")) {
534 printf("Sorry, this demo requires GL_EXT_framebuffer_object or GL_SGIS_generate_mipmap\n");
535 exit(0);
536 }
537 }
538 printf("GL_RENDERER: %s\n", (char *) glGetString(GL_RENDERER));
539
540 if (useImageFiles) {
541 load_envmaps();
542 }
543 else {
544 init_checkers();
545 }
546
547 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
548 glEnable(GL_DEPTH_TEST);
549
550 glClearColor(.3, .3, .3, 0);
551 glColor3f( 1.0, 1.0, 1.0 );
552
553 set_mode(0);
554 }
555
556
557 static void usage(void)
558 {
559 printf("keys:\n");
560 printf(" SPACE - toggle animation\n");
561 printf(" CURSOR KEYS - rotation\n");
562 printf(" c - toggle texture clamp/wrap mode\n");
563 printf(" f - toggle texture filter mode\n");
564 printf(" m - toggle texgen reflection mode\n");
565 printf(" z/Z - change viewing distance\n");
566 }
567
568
569 static void parse_args(int argc, char *argv[])
570 {
571 int initFlag = 0;
572 int i;
573
574 for (i = 1; i < argc; i++) {
575 if (strcmp(argv[i], "-i") == 0)
576 initFlag = 1;
577 else if (strcmp(argv[i], "--noclear") == 0)
578 NoClear = GL_TRUE;
579 else {
580 fprintf(stderr, "Bad option: %s\n", argv[i]);
581 exit(1);
582 }
583 }
584 init (initFlag);
585 }
586
587 int main( int argc, char *argv[] )
588 {
589 glutInit(&argc, argv);
590 glutInitWindowPosition(0, 0);
591 glutInitWindowSize(600, 500);
592 glutInitDisplayMode( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE );
593 glutCreateWindow("Texture Cube Mapping");
594 glewInit();
595 glutReshapeFunc( reshape );
596 glutKeyboardFunc( key );
597 glutSpecialFunc( specialkey );
598 glutDisplayFunc( draw );
599 if (anim)
600 glutIdleFunc(idle);
601 parse_args(argc, argv);
602 usage();
603 glutMainLoop();
604 return 0;
605 }