| /* |
| * 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; |
| } |