/* * Test mipmap generation and lod bias. * * Brian Paul * 17 March 2008 */ #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <math.h> #include <GL/glew.h> #include <GL/glut.h> #include <GL/glext.h> #include "readtex.h" #define TEXTURE_FILE "../images/arch.rgb" #define LEVELS 8 #define SIZE (1<<LEVELS) static int TexWidth = SIZE, TexHeight = SIZE; static int WinWidth = 1044, WinHeight = 900; static GLfloat Bias = 0.0; static GLboolean ScaleQuads = GL_FALSE; static GLboolean Linear = GL_FALSE; static GLint Win = 0; static GLint RenderTextureLevel = 0; static GLuint TexObj; static void CheckError(int line) { GLenum err = glGetError(); if (err) { printf("GL Error 0x%x at line %d\n", (int) err, line); } } static void PrintString(const char *s) { while (*s) { glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (int) *s); s++; } } static void MipGenTexture( void ) { /* test auto mipmap generation */ GLint width, height, i; GLenum format; GLubyte *image = LoadRGBImage(TEXTURE_FILE, &width, &height, &format); if (!image) { printf("Error: could not load texture image %s\n", TEXTURE_FILE); exit(1); } /* resize to TexWidth x TexHeight */ if (width != TexWidth || height != TexHeight) { GLubyte *newImage = malloc(TexWidth * TexHeight * 4); fprintf(stderr, "rescale %d %d to %d %d\n", width, height, TexWidth, TexHeight); fflush(stderr); gluScaleImage(format, width, height, GL_UNSIGNED_BYTE, image, TexWidth, TexHeight, GL_UNSIGNED_BYTE, newImage); free(image); image = newImage; } printf("Using GL_SGIS_generate_mipmap\n"); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); glTexImage2D(GL_TEXTURE_2D, 0, format, TexWidth, TexHeight, 0, format, GL_UNSIGNED_BYTE, image); free(image); /* make sure mipmap was really generated correctly */ width = TexWidth; height = TexHeight; for (i = 0; i < 9; i++) { GLint w, h; glGetTexLevelParameteriv(GL_TEXTURE_2D, i, GL_TEXTURE_WIDTH, &w); glGetTexLevelParameteriv(GL_TEXTURE_2D, i, GL_TEXTURE_HEIGHT, &h); printf("Level %d size: %d x %d\n", i, w, h); assert(w == width); assert(h == height); width /= 2; height /= 2; } glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); } static void ResetTextureLevel( int i ) { GLubyte tex2d[SIZE*SIZE][4]; { GLint Width = TexWidth / (1 << i); GLint Height = TexHeight / (1 << i); GLint s, t; for (s = 0; s < Width; s++) { for (t = 0; t < Height; t++) { tex2d[t*Width+s][0] = ((s / 16) % 2) ? 0 : 255; tex2d[t*Width+s][1] = ((t / 16) % 2) ? 0 : 255; tex2d[t*Width+s][2] = 128; tex2d[t*Width+s][3] = 255; } } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, i, GL_RGB, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex2d); } } static void ResetTexture( void ) { #if 0 /* This doesn't work so well as the arch texture is 512x512. */ LoadRGBMipmaps(TEXTURE_FILE, GL_RGB); #else { int i; for (i = 0; i <= LEVELS; i++) { ResetTextureLevel(i); } } #endif } static void RenderTexture( void ) { GLenum status; GLuint MyFB; fprintf(stderr, "RenderTextureLevel %d\n", RenderTextureLevel); fflush(stderr); /* gen framebuffer id, delete it, do some assertions, just for testing */ glGenFramebuffersEXT(1, &MyFB); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, MyFB); assert(glIsFramebufferEXT(MyFB)); CheckError(__LINE__); /* Render color to texture */ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, TexObj, RenderTextureLevel); CheckError(__LINE__); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -15.0); status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { printf("Framebuffer incomplete!!!\n"); } glViewport(0, 0, TexWidth / (1 << RenderTextureLevel), TexHeight / (1 << RenderTextureLevel)); glClearColor(0.5, 0.5, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); CheckError(__LINE__); glBegin(GL_POLYGON); glColor3f(1, 0, 0); glVertex2f(-1, -1); glColor3f(0, 1, 0); glVertex2f(1, -1); glColor3f(0, 0, 1); glVertex2f(0, 1); glEnd(); /* Bind normal framebuffer */ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); CheckError(__LINE__); glDeleteFramebuffersEXT(1, &MyFB); CheckError(__LINE__); glClearColor(0, 0, 0, 0); } static void Display(void) { int x, y, bias; char str[100]; int texWidth = TexWidth, texHeight = TexHeight; glViewport(0, 0, WinHeight, WinHeight); glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, WinWidth, 0, WinHeight, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f(1,1,1); if (Linear) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } y = WinHeight - 300; x = 4; for (bias = -1; bias < 11; bias++) { if (ScaleQuads) { if (bias > 0) { if (texWidth == 1 && texHeight == 1) break; texWidth = TexWidth >> bias; texHeight = TexHeight >> bias; if (texWidth < 1) texWidth = 1; if (texHeight < 1) texHeight = 1; } glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0.0); } else { glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, bias); } glRasterPos2f(x, y + TexHeight + 5); if (ScaleQuads) sprintf(str, "Texture Level %d: %d x %d", (bias < 0 ? 0 : bias), texWidth, texHeight); else sprintf(str, "Texture LOD Bias = %d", bias); PrintString(str); glPushMatrix(); glTranslatef(x, y, 0); glEnable(GL_TEXTURE_2D); glBegin(GL_POLYGON); glTexCoord2f(0, 0); glVertex2f(0, 0); glTexCoord2f(1, 0); glVertex2f(texWidth, 0); glTexCoord2f(1, 1); glVertex2f(texWidth, texHeight); glTexCoord2f(0, 1); glVertex2f(0, texHeight); glEnd(); glPopMatrix(); glDisable(GL_TEXTURE_2D); x += TexWidth + 4; if (x >= WinWidth) { x = 4; y -= 300; } } glutSwapBuffers(); } static void Reshape(int width, int height) { WinWidth = width; WinHeight = height; } static void Key(unsigned char key, int x, int y) { (void) x; (void) y; switch (key) { case 'b': Bias -= 10; break; case 'B': Bias += 10; break; case 'l': Linear = !Linear; break; case 'v': RenderTextureLevel++; break; case 'V': RenderTextureLevel--; break; case 'r': RenderTexture(); break; case 'X': ResetTexture(); break; case 'x': ResetTextureLevel(RenderTextureLevel); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': Bias = 100.0 * (key - '0'); break; case 's': ScaleQuads = !ScaleQuads; break; case ' ': MipGenTexture(); Bias = 0; Linear = 0; RenderTextureLevel = 0; ScaleQuads = 0; break; case 27: glutDestroyWindow(Win); exit(0); break; } glutPostRedisplay(); } static void Init(void) { GLfloat maxBias; if (!glutExtensionSupported("GL_EXT_texture_lod_bias")) { printf("Sorry, GL_EXT_texture_lod_bias not supported by this renderer.\n"); exit(1); } if (!glutExtensionSupported("GL_SGIS_generate_mipmap")) { printf("Sorry, GL_SGIS_generate_mipmap not supported by this renderer.\n"); exit(1); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &TexObj); glBindTexture(GL_TEXTURE_2D, TexObj); if (1) MipGenTexture(); else ResetTexture(); /* mipmapping required for this extension */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS_EXT, &maxBias); printf("GL_RENDERER: %s\n", (char*) glGetString(GL_RENDERER)); printf("LOD bias range: [%g, %g]\n", -maxBias, maxBias); printf("Press 's' to toggle quad scaling\n"); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitWindowPosition(0, 0); glutInitWindowSize(WinWidth, WinHeight); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); Win = glutCreateWindow(argv[0]); glewInit(); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutDisplayFunc(Display); Init(); glutMainLoop(); return 0; }