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