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