| /* |
| * (C) Copyright IBM Corporation 2005 |
| * All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * on the rights to use, copy, modify, merge, publish, distribute, sub |
| * license, and/or sell copies of the Software, and to permit persons to whom |
| * the Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
| * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, |
| * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| * USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| #include <GL/glut.h> |
| |
| const GLenum filter_modes[] = { |
| GL_NEAREST, |
| GL_LINEAR, |
| GL_NEAREST_MIPMAP_NEAREST, |
| GL_NEAREST_MIPMAP_LINEAR, |
| GL_LINEAR_MIPMAP_NEAREST, |
| GL_LINEAR_MIPMAP_LINEAR, |
| }; |
| |
| static GLenum min_filter = GL_LINEAR_MIPMAP_LINEAR; |
| static GLenum mag_filter = GL_LINEAR; |
| |
| static unsigned segments = 64; |
| static GLfloat * position_data = NULL; |
| static GLfloat * texcoord_data = NULL; |
| static GLfloat max_anisotropy = 0.0; |
| static GLfloat anisotropy = 1.0; |
| |
| static void generate_tunnel( unsigned num_segs, GLfloat ** pos_data, |
| GLfloat ** tex_data ); |
| static void generate_textures( unsigned mode ); |
| |
| #define min(a,b) ( (a) < (b) ) ? (a) : (b) |
| #define max(a,b) ( (a) > (b) ) ? (a) : (b) |
| |
| |
| static void Display( void ) |
| { |
| glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter ); |
| glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter ); |
| |
| if ( max_anisotropy > 0.0 ) { |
| glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, |
| anisotropy ); |
| } |
| |
| glClear( GL_COLOR_BUFFER_BIT ); |
| glLoadIdentity(); |
| glTranslatef( 0.0f, 0.0f, -19.0f ); |
| |
| glVertexPointer( 4, GL_FLOAT, 0, position_data ); |
| glTexCoordPointer( 2, GL_FLOAT, 0, texcoord_data ); |
| glEnableClientState( GL_VERTEX_ARRAY ); |
| glEnableClientState( GL_TEXTURE_COORD_ARRAY ); |
| glDrawArrays( GL_QUADS, 0, 4 * segments ); |
| glutSwapBuffers(); |
| } |
| |
| |
| static void Reshape( int width, int height ) |
| { |
| glViewport(0, 0, width, height); |
| glMatrixMode(GL_PROJECTION); |
| glLoadIdentity(); |
| gluPerspective(45.0f, (GLfloat)(width)/(GLfloat)(height), 0.1f, 100.0f); |
| glMatrixMode(GL_MODELVIEW); |
| glLoadIdentity(); |
| } |
| |
| |
| static void Key( unsigned char key, int x, int y ) |
| { |
| GLfloat new_anisotropy = anisotropy; |
| |
| (void) x; |
| (void) y; |
| |
| |
| switch( key ) { |
| case 'a': { |
| new_anisotropy = anisotropy - 1.0; |
| break; |
| } |
| |
| case 'A': { |
| new_anisotropy = anisotropy + 1.0; |
| break; |
| } |
| |
| case 's': { |
| segments--; |
| if ( segments < 3 ) { |
| segments = 3; |
| } |
| generate_tunnel( segments, & position_data, & texcoord_data ); |
| break; |
| } |
| |
| case 'S': { |
| segments++; |
| if ( segments > 128 ) { |
| segments = 128; |
| } |
| generate_tunnel( segments, & position_data, & texcoord_data ); |
| break; |
| } |
| case 'q': |
| case 'Q': |
| case 27: |
| exit(0); |
| break; |
| } |
| |
| new_anisotropy = max( new_anisotropy, 1.0 ); |
| new_anisotropy = min( new_anisotropy, max_anisotropy ); |
| if ( new_anisotropy != anisotropy ) { |
| anisotropy = new_anisotropy; |
| printf( "Texture anisotropy: %f%s\n", anisotropy, |
| (anisotropy == 1.0) ? " (disabled)" : "" ); |
| } |
| |
| glutPostRedisplay(); |
| } |
| |
| |
| static void SpecialKey( int key, int x, int y ) |
| { |
| (void) x; |
| (void) y; |
| (void) key; |
| glutPostRedisplay(); |
| } |
| |
| |
| static void menu_handler( int selection ) |
| { |
| switch( selection >> 3 ) { |
| case 0: |
| glBindTexture( GL_TEXTURE_2D, selection ); |
| break; |
| |
| case 1: |
| min_filter = filter_modes[ selection & 7 ]; |
| break; |
| |
| case 2: |
| mag_filter = filter_modes[ selection & 7 ]; |
| break; |
| } |
| |
| glutPostRedisplay(); |
| } |
| |
| |
| static void Init( void ) |
| { |
| glDisable(GL_CULL_FACE); |
| glEnable(GL_TEXTURE_2D); |
| glClearColor(0.0f, 0.0f, 0.4f, 0.0f); |
| glShadeModel(GL_SMOOTH); |
| glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); |
| |
| generate_tunnel( segments, & position_data, & texcoord_data ); |
| |
| glBindTexture( GL_TEXTURE_2D, 1 ); |
| generate_textures(1); |
| |
| glBindTexture( GL_TEXTURE_2D, 2 ); |
| generate_textures(2); |
| |
| glBindTexture( GL_TEXTURE_2D, 3 ); |
| generate_textures(3); |
| |
| if ( glutExtensionSupported( "GL_EXT_texture_filter_anisotropic" ) ) { |
| glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, & max_anisotropy ); |
| } |
| |
| printf("Maximum texture anisotropy: %f\n", max_anisotropy ); |
| |
| /* Create the menus. */ |
| |
| glutCreateMenu( menu_handler ); |
| glutAddMenuEntry( "Min filter: GL_NEAREST", 8 + 0 ); |
| glutAddMenuEntry( "Min filter: GL_LINEAR", 8 + 1 ); |
| glutAddMenuEntry( "Min filter: GL_NEAREST_MIMMAP_NEAREST", 8 + 2 ); |
| glutAddMenuEntry( "Min filter: GL_NEAREST_MIMMAP_LINEAR", 8 + 3 ); |
| glutAddMenuEntry( "Min filter: GL_LINEAR_MIMMAP_NEAREST", 8 + 4 ); |
| glutAddMenuEntry( "Min filter: GL_LINEAR_MIMMAP_LINEAR", 8 + 5 ); |
| glutAddMenuEntry( "Mag filter: GL_NEAREST", 16 + 0 ); |
| glutAddMenuEntry( "Mag filter: GL_LINEAR", 16 + 1 ); |
| glutAddMenuEntry( "Texture: regular mipmaps", 1 ); |
| glutAddMenuEntry( "Texture: blended mipmaps", 2 ); |
| glutAddMenuEntry( "Texture: color mipmaps", 3 ); |
| glutAttachMenu( GLUT_RIGHT_BUTTON ); |
| } |
| |
| |
| static void generate_tunnel( unsigned num_segs, GLfloat ** pos_data, |
| GLfloat ** tex_data ) |
| { |
| const GLfloat far = 20.0f; |
| const GLfloat near = -90.0f; |
| const GLfloat far_tex = 30.0f; |
| const GLfloat near_tex = 0.0f; |
| const GLfloat angle_step = (2 * M_PI) / num_segs; |
| const GLfloat tex_coord_step = 2.0 / num_segs; |
| GLfloat angle = 0.0f; |
| GLfloat tex_coord = 0.0f; |
| unsigned i; |
| GLfloat * position; |
| GLfloat * texture; |
| |
| |
| position = realloc( *pos_data, sizeof( GLfloat ) * num_segs * 4 * 4 ); |
| texture = realloc( *tex_data, sizeof( GLfloat ) * num_segs * 4 * 2 ); |
| |
| *pos_data = position; |
| *tex_data = texture; |
| |
| for ( i = 0 ; i < num_segs ; i++ ) { |
| position[0] = 2.5 * sinf( angle ); |
| position[1] = 2.5 * cosf( angle ); |
| position[2] = (i & 1) ? far : near; |
| position[3] = 1.0f; |
| |
| position[4] = position[0]; |
| position[5] = position[1]; |
| position[6] = (i & 1) ? near : far; |
| position[7] = 1.0f; |
| |
| position += 8; |
| |
| texture[0] = tex_coord; |
| texture[1] = (i & 1) ? far_tex : near_tex; |
| texture += 2; |
| |
| texture[0] = tex_coord; |
| texture[1] = (i & 1) ? near_tex : far_tex; |
| texture += 2; |
| |
| angle += angle_step; |
| tex_coord += tex_coord_step; |
| |
| position[0] = 2.5 * sinf( angle ); |
| position[1] = 2.5 * cosf( angle ); |
| position[2] = (i & 1) ? near : far; |
| position[3] = 1.0f; |
| |
| position[4] = position[0]; |
| position[5] = position[1]; |
| position[6] = (i & 1) ? far : near; |
| position[7] = 1.0f; |
| |
| position += 8; |
| |
| texture[0] = tex_coord; |
| texture[1] = (i & 1) ? near_tex : far_tex; |
| texture += 2; |
| |
| texture[0] = tex_coord; |
| texture[1] = (i & 1) ? far_tex : near_tex; |
| texture += 2; |
| } |
| } |
| |
| |
| static void generate_textures( unsigned mode ) |
| { |
| #define LEVEL_COLORS 6 |
| const GLfloat colors[LEVEL_COLORS][3] = { |
| { 1.0, 0.0, 0.0 }, /* 32 x 32 */ |
| { 0.0, 1.0, 0.0 }, /* 16 x 16 */ |
| { 0.0, 0.0, 1.0 }, /* 8 x 8 */ |
| { 1.0, 0.0, 1.0 }, /* 4 x 4 */ |
| { 1.0, 1.0, 1.0 }, /* 2 x 2 */ |
| { 1.0, 1.0, 0.0 } /* 1 x 1 */ |
| }; |
| const unsigned checkers_per_level = 2; |
| GLfloat * tex; |
| unsigned level; |
| unsigned size; |
| GLint max_size; |
| |
| |
| glGetIntegerv( GL_MAX_TEXTURE_SIZE, & max_size ); |
| if ( max_size > 512 ) { |
| max_size = 512; |
| } |
| |
| tex = malloc( sizeof( GLfloat ) * 3 * max_size * max_size ); |
| |
| level = 0; |
| for ( size = max_size ; size > 0 ; size >>= 1 ) { |
| unsigned divisor = size / checkers_per_level; |
| unsigned i; |
| unsigned j; |
| GLfloat checkers[2][3]; |
| |
| |
| if ((level == 0) || (mode == 1)) { |
| checkers[0][0] = 1.0; |
| checkers[0][1] = 1.0; |
| checkers[0][2] = 1.0; |
| checkers[1][0] = 0.0; |
| checkers[1][1] = 0.0; |
| checkers[1][2] = 0.0; |
| } |
| else if (mode == 2) { |
| checkers[0][0] = colors[level % LEVEL_COLORS][0]; |
| checkers[0][1] = colors[level % LEVEL_COLORS][1]; |
| checkers[0][2] = colors[level % LEVEL_COLORS][2]; |
| checkers[1][0] = colors[level % LEVEL_COLORS][0] * 0.5; |
| checkers[1][1] = colors[level % LEVEL_COLORS][1] * 0.5; |
| checkers[1][2] = colors[level % LEVEL_COLORS][2] * 0.5; |
| } |
| else { |
| checkers[0][0] = colors[level % LEVEL_COLORS][0]; |
| checkers[0][1] = colors[level % LEVEL_COLORS][1]; |
| checkers[0][2] = colors[level % LEVEL_COLORS][2]; |
| checkers[1][0] = colors[level % LEVEL_COLORS][0]; |
| checkers[1][1] = colors[level % LEVEL_COLORS][1]; |
| checkers[1][2] = colors[level % LEVEL_COLORS][2]; |
| } |
| |
| if ( divisor == 0 ) { |
| divisor = 1; |
| |
| checkers[0][0] = (checkers[0][0] + checkers[1][0]) / 2; |
| checkers[0][1] = (checkers[0][0] + checkers[1][0]) / 2; |
| checkers[0][2] = (checkers[0][0] + checkers[1][0]) / 2; |
| checkers[1][0] = checkers[0][0]; |
| checkers[1][1] = checkers[0][1]; |
| checkers[1][2] = checkers[0][2]; |
| } |
| |
| |
| for ( i = 0 ; i < size ; i++ ) { |
| for ( j = 0 ; j < size ; j++ ) { |
| const unsigned idx = ((i ^ j) / divisor) & 1; |
| |
| tex[ ((i * size) + j) * 3 + 0] = checkers[ idx ][0]; |
| tex[ ((i * size) + j) * 3 + 1] = checkers[ idx ][1]; |
| tex[ ((i * size) + j) * 3 + 2] = checkers[ idx ][2]; |
| } |
| } |
| |
| glTexImage2D( GL_TEXTURE_2D, level, GL_RGB, size, size, 0, |
| GL_RGB, GL_FLOAT, tex ); |
| level++; |
| } |
| |
| free( tex ); |
| } |
| |
| |
| int main( int argc, char ** argv ) |
| { |
| glutInit( &argc, argv ); |
| glutInitWindowPosition( 0, 0 ); |
| glutInitWindowSize( 800, 600 ); |
| glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE ); |
| glutCreateWindow( "Texture Filter Test" ); |
| glutReshapeFunc( Reshape ); |
| glutKeyboardFunc( Key ); |
| glutSpecialFunc( SpecialKey ); |
| glutDisplayFunc( Display ); |
| |
| Init(); |
| |
| printf("\nUse the right-button menu to select the texture and filter mode.\n"); |
| printf("Use 'A' and 'a' to increase and decrease the aniotropy.\n"); |
| printf("Use 'S' and 's' to increase and decrease the number of cylinder segments.\n"); |
| printf("Use 'q' to exit.\n\n"); |
| |
| glutMainLoop(); |
| return 0; |
| } |