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