diff --git a/doomsday/client/include/gl/gl_texmanager.h b/doomsday/client/include/gl/gl_texmanager.h index c932407b02..12eafafd2a 100644 --- a/doomsday/client/include/gl/gl_texmanager.h +++ b/doomsday/client/include/gl/gl_texmanager.h @@ -86,55 +86,8 @@ void GL_ResetTextureManager(); void GL_TexReset(); -/** - * Change the GL minification filter for all prepared textures. - */ -void GL_SetAllTexturesMinFilter(int minFilter); - -/** - * Change the GL minification filter for all prepared "raw" textures. - */ -void GL_SetRawTexturesMinFilter(int minFilter); - -/// Release all textures used with 'Raw Images'. -void GL_ReleaseTexturesForRawImages(); - void GL_PruneTextureVariantSpecifications(); -/** - * Prepares all the system textures (dlight, ptcgens). - */ -void GL_LoadSystemTextures(); - -void GL_DeleteAllLightingSystemTextures(); - -/** - * @param glFormat Identifier of the desired GL texture format. - * @param loadFormat Identifier of the GL texture format used during upload. - * @param pixels Texture pixel data to be uploaded. - * @param width Width of the texture in pixels. - * @param height Height of the texture in pixels. - * @param genMipmaps If negative sets a specific mipmap level, e.g.: - * @c -1, means mipmap level 1. - * - * @return @c true iff successful. - */ -boolean GL_UploadTexture(int glFormat, int loadFormat, uint8_t const *pixels, - int width, int height, int genMipmaps); - -/** - * @param glFormat Identifier of the desired GL texture format. - * @param loadFormat Identifier of the GL texture format used during upload. - * @param pixels Texture pixel data to be uploaded. - * @param width Width of the texture in pixels. - * @param height Height of the texture in pixels. - * @param grayFactor Strength of the blend where @c 0:none @c 1:full. - * - * @return @c true iff successful. - */ -boolean GL_UploadTextureGrayMipmap(int glFormat, int loadFormat, uint8_t const *pixels, - int width, int height, float grayFactor); - /** * Prepare a TextureVariantSpecification according to usage context. If incomplete * context information is supplied, suitable defaults are chosen in their place. @@ -165,12 +118,27 @@ texturevariantspecification_t &GL_DetailTextureSpec(float contrast); * revised texture management APIs. */ +/** + * Prepares all the system textures (dlight, ptcgens). + */ +void GL_LoadSystemTextures(); + +void GL_DeleteAllLightingSystemTextures(); + DGLuint GL_PrepareFlaremap(de::Uri const &resourceUri); DGLuint GL_PrepareSysFlaremap(flaretexid_t which); DGLuint GL_PrepareLSTexture(lightingtexid_t which); DGLuint GL_PrepareRawTexture(rawtex_t &rawTex); +/** + * Change the GL minification filter for all prepared "raw" textures. + */ +void GL_SetRawTexturesMinFilter(int minFilter); + +/// Release all textures used with 'Raw Images'. +void GL_ReleaseTexturesForRawImages(); + DGLuint GL_NewTextureWithParams(dgltexformat_t format, int width, int height, uint8_t const *pixels, int flags); DGLuint GL_NewTextureWithParams(dgltexformat_t format, int width, int height, uint8_t const *pixels, int flags, int grayMipmap, int minFilter, int magFilter, int anisoFilter, int wrapS, int wrapT); diff --git a/doomsday/client/src/gl/gl_texmanager.cpp b/doomsday/client/src/gl/gl_texmanager.cpp index 049d368f07..ef1927d8f3 100644 --- a/doomsday/client/src/gl/gl_texmanager.cpp +++ b/doomsday/client/src/gl/gl_texmanager.cpp @@ -703,226 +703,6 @@ void GL_PruneTextureVariantSpecifications() #endif } -/** - * Given a pixel format return the number of bytes to store one pixel. - * @pre Input data is of GL_UNSIGNED_BYTE type. - */ -static int BytesPerPixel(GLint format) -{ - switch(format) - { - case GL_COLOR_INDEX: - case GL_STENCIL_INDEX: - case GL_DEPTH_COMPONENT: - case GL_RED: - case GL_GREEN: - case GL_BLUE: - case GL_ALPHA: - case GL_LUMINANCE: return 1; - - case GL_LUMINANCE_ALPHA: return 2; - - case GL_RGB: - case GL_RGB8: - case GL_BGR: return 3; - - case GL_RGBA: - case GL_RGBA8: - case GL_BGRA: return 4; - - default: - Con_Error("BytesPerPixel: Unknown format %i.", (int) format); - return 0; // Unreachable. - } -} - -boolean GL_UploadTextureGrayMipmap(int glFormat, int loadFormat, const uint8_t* pixels, - int width, int height, float grayFactor) -{ - int i, w, h, numpels = width * height, numLevels, pixelSize; - uint8_t* image, *faded, *out; - const uint8_t* in; - float invFactor; - DENG_ASSERT(pixels); - - if(!(GL_RGB == loadFormat || GL_LUMINANCE == loadFormat)) - Con_Error("GL_UploadTextureGrayMipmap: Unsupported load format %i.", (int) loadFormat); - - pixelSize = (loadFormat == GL_LUMINANCE? 1 : 3); - - // Can't operate on null texture. - if(width < 1 || height < 1) - return false; - - // Check that the texture dimensions are valid. - if(!GL_state.features.texNonPowTwo && - (width != M_CeilPow2(width) || height != M_CeilPow2(height))) - return false; - - if(width > GL_state.maxTexSize || height > GL_state.maxTexSize) - return false; - - numLevels = GL_NumMipmapLevels(width, height); - grayFactor = MINMAX_OF(0, grayFactor, 1); - invFactor = 1 - grayFactor; - - // Buffer used for the faded texture. - faded = (uint8_t*) M_Malloc(numpels / 4); - image = (uint8_t*) M_Malloc(numpels); - - // Initial fading. - in = pixels; - out = image; - for(i = 0; i < numpels; ++i) - { - *out++ = (uint8_t) MINMAX_OF(0, (*in * grayFactor + 127 * invFactor), 255); - in += pixelSize; - } - - // Upload the first level right away. - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, width, height, 0, (GLint)loadFormat, - GL_UNSIGNED_BYTE, image); - - // Generate all mipmaps levels. - w = width; - h = height; - for(i = 0; i < numLevels; ++i) - { - GL_DownMipmap8(image, faded, w, h, (i * 1.75f) / numLevels); - - // Go down one level. - if(w > 1) - w /= 2; - if(h > 1) - h /= 2; - - glTexImage2D(GL_TEXTURE_2D, i + 1, glFormat, w, h, 0, (GLint)loadFormat, - GL_UNSIGNED_BYTE, faded); - } - - // Do we need to free the temp buffer? - M_Free(faded); - M_Free(image); - - DENG_ASSERT(!Sys_GLCheckError()); - return true; -} - -boolean GL_UploadTexture(int glFormat, int loadFormat, const uint8_t* pixels, - int width, int height, int genMipmaps) -{ - const int packRowLength = 0, packAlignment = 1, packSkipRows = 0, packSkipPixels = 0; - const int unpackRowLength = 0, unpackAlignment = 1, unpackSkipRows = 0, unpackSkipPixels = 0; - int mipLevel = 0; - DENG_ASSERT(pixels); - - if(!(GL_LUMINANCE_ALPHA == loadFormat || GL_LUMINANCE == loadFormat || - GL_RGB == loadFormat || GL_RGBA == loadFormat)) - Con_Error("GL_UploadTexture: Unsupported load format %i.", (int) loadFormat); - - // Can't operate on null texture. - if(width < 1 || height < 1) - return false; - - // Check that the texture dimensions are valid. - if(width > GL_state.maxTexSize || height > GL_state.maxTexSize) - return false; - - if(!GL_state.features.texNonPowTwo && - (width != M_CeilPow2(width) || height != M_CeilPow2(height))) - return false; - - // Negative indices signify a specific mipmap level is being uploaded. - if(genMipmaps < 0) - { - mipLevel = -genMipmaps; - genMipmaps = 0; - } - - DENG_ASSERT_IN_MAIN_THREAD(); - DENG_ASSERT_GL_CONTEXT_ACTIVE(); - - // Automatic mipmap generation? - if(GL_state.extensions.genMipmapSGIS && genMipmaps) - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); - - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)packRowLength); - glPixelStorei(GL_PACK_ALIGNMENT, (GLint)packAlignment); - glPixelStorei(GL_PACK_SKIP_ROWS, (GLint)packSkipRows); - glPixelStorei(GL_PACK_SKIP_PIXELS, (GLint)packSkipPixels); - glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)unpackRowLength); - glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)unpackAlignment); - glPixelStorei(GL_UNPACK_SKIP_ROWS, (GLint)unpackSkipRows); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, (GLint)unpackSkipPixels); - - if(genMipmaps && !GL_state.extensions.genMipmapSGIS) - { // Build all mipmap levels. - int neww, newh, bpp, w, h; - void* image, *newimage; - - bpp = BytesPerPixel(loadFormat); - if(bpp == 0) - Con_Error("GL_UploadTexture: Unknown GL format %i.\n", (int) loadFormat); - - GL_OptimalTextureSize(width, height, false, true, &w, &h); - - if(w != width || h != height) - { - // Must rescale image to get "top" mipmap texture image. - image = GL_ScaleBufferEx(pixels, width, height, bpp, /*GL_UNSIGNED_BYTE,*/ - unpackRowLength, unpackAlignment, unpackSkipRows, unpackSkipPixels, - w, h, /*GL_UNSIGNED_BYTE,*/ packRowLength, packAlignment, packSkipRows, - packSkipPixels); - if(NULL == image) - Con_Error("GL_UploadTexture: Unknown error resizing mipmap level #0."); - } - else - { - image = (void*) pixels; - } - - for(;;) - { - glTexImage2D(GL_TEXTURE_2D, mipLevel, (GLint)glFormat, w, h, 0, (GLint)loadFormat, - GL_UNSIGNED_BYTE, image); - - if(w == 1 && h == 1) - break; - - ++mipLevel; - neww = (w < 2) ? 1 : w / 2; - newh = (h < 2) ? 1 : h / 2; - newimage = GL_ScaleBufferEx(image, w, h, bpp, /*GL_UNSIGNED_BYTE,*/ - unpackRowLength, unpackAlignment, unpackSkipRows, unpackSkipPixels, - neww, newh, /*GL_UNSIGNED_BYTE,*/ packRowLength, packAlignment, - packSkipRows, packSkipPixels); - if(!newimage) - Con_Error("GL_UploadTexture: Unknown error resizing mipmap level #%i.", mipLevel); - - if(image != pixels) - M_Free(image); - image = newimage; - - w = neww; - h = newh; - } - - if(image != pixels) - M_Free(image); - } - else - { - glTexImage2D(GL_TEXTURE_2D, mipLevel, (GLint)glFormat, (GLsizei)width, - (GLsizei)height, 0, (GLint)loadFormat, GL_UNSIGNED_BYTE, pixels); - } - - glPopClientAttrib(); - DENG_ASSERT(!Sys_GLCheckError()); - - return true; -} - DGLuint GL_PrepareLSTexture(lightingtexid_t which) { if(novideo) return 0; @@ -1147,7 +927,7 @@ void GL_DoUpdateTexParams() { int newMinFilter = glmode[mipmapping]; - GL_SetAllTexturesMinFilter(newMinFilter); + //GL_SetAllTexturesMinFilter(newMinFilter); GL_SetRawTexturesMinFilter(newMinFilter); } @@ -1221,12 +1001,6 @@ void GL_ReleaseTexturesForRawImages() Z_Free(rawTexs); } -void GL_SetAllTexturesMinFilter(int /*minFilter*/) -{ - /// @todo This is no longer correct logic. Changing the global minification - /// filter should not modify the uploaded texture content. -} - DGLuint GL_NewTextureWithParams(dgltexformat_t format, int width, int height, uint8_t const *pixels, int flags) { @@ -1277,7 +1051,7 @@ D_CMD(LowRes) filterUI = 0; texMagMode = 0; - GL_SetAllTexturesMinFilter(GL_NEAREST); + //GL_SetAllTexturesMinFilter(GL_NEAREST); GL_SetRawTexturesMinFilter(GL_NEAREST); // And do a texreset so everything is updated. @@ -1314,6 +1088,6 @@ D_CMD(MipMap) } mipmapping = newMipMode; - GL_SetAllTexturesMinFilter(glmode[mipmapping]); + //GL_SetAllTexturesMinFilter(glmode[mipmapping]); return true; } diff --git a/doomsday/client/src/gl/texturecontent.cpp b/doomsday/client/src/gl/texturecontent.cpp index 8461d4f591..db277ef769 100644 --- a/doomsday/client/src/gl/texturecontent.cpp +++ b/doomsday/client/src/gl/texturecontent.cpp @@ -51,6 +51,39 @@ static int BytesPerPixelFmt(dgltexformat_t format) } } +/** + * Given a pixel format return the number of bytes to store one pixel. + * @pre Input data is of GL_UNSIGNED_BYTE type. + */ +static int BytesPerPixel(GLint format) +{ + switch(format) + { + case GL_COLOR_INDEX: + case GL_STENCIL_INDEX: + case GL_DEPTH_COMPONENT: + case GL_RED: + case GL_GREEN: + case GL_BLUE: + case GL_ALPHA: + case GL_LUMINANCE: return 1; + + case GL_LUMINANCE_ALPHA: return 2; + + case GL_RGB: + case GL_RGB8: + case GL_BGR: return 3; + + case GL_RGBA: + case GL_RGBA8: + case GL_BGRA: return 4; + + default: + Con_Error("BytesPerPixel: Unknown format %i.", (int) format); + return 0; // Unreachable. + } +} + void GL_InitTextureContent(texturecontent_t *content) { DENG_ASSERT(content); @@ -405,6 +438,218 @@ static GLint ChooseTextureFormat(dgltexformat_t format, boolean allowCompression } } +/** + * @param glFormat Identifier of the desired GL texture format. + * @param loadFormat Identifier of the GL texture format used during upload. + * @param pixels Texture pixel data to be uploaded. + * @param width Width of the texture in pixels. + * @param height Height of the texture in pixels. + * @param genMipmaps If negative sets a specific mipmap level, e.g.: + * @c -1, means mipmap level 1. + * + * @return @c true iff successful. + */ +static boolean uploadTexture(int glFormat, int loadFormat, const uint8_t* pixels, + int width, int height, int genMipmaps) +{ + const int packRowLength = 0, packAlignment = 1, packSkipRows = 0, packSkipPixels = 0; + const int unpackRowLength = 0, unpackAlignment = 1, unpackSkipRows = 0, unpackSkipPixels = 0; + int mipLevel = 0; + DENG_ASSERT(pixels); + + if(!(GL_LUMINANCE_ALPHA == loadFormat || GL_LUMINANCE == loadFormat || + GL_RGB == loadFormat || GL_RGBA == loadFormat)) + { + throw Error("texturecontent_t::uploadTexture", "Unsupported load format " + String::number(loadFormat)); + } + + // Can't operate on null texture. + if(width < 1 || height < 1) + return false; + + // Check that the texture dimensions are valid. + if(width > GL_state.maxTexSize || height > GL_state.maxTexSize) + return false; + + if(!GL_state.features.texNonPowTwo && + (width != M_CeilPow2(width) || height != M_CeilPow2(height))) + return false; + + // Negative indices signify a specific mipmap level is being uploaded. + if(genMipmaps < 0) + { + mipLevel = -genMipmaps; + genMipmaps = 0; + } + + DENG_ASSERT_IN_MAIN_THREAD(); + DENG_ASSERT_GL_CONTEXT_ACTIVE(); + + // Automatic mipmap generation? + if(GL_state.extensions.genMipmapSGIS && genMipmaps) + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)packRowLength); + glPixelStorei(GL_PACK_ALIGNMENT, (GLint)packAlignment); + glPixelStorei(GL_PACK_SKIP_ROWS, (GLint)packSkipRows); + glPixelStorei(GL_PACK_SKIP_PIXELS, (GLint)packSkipPixels); + glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)unpackRowLength); + glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)unpackAlignment); + glPixelStorei(GL_UNPACK_SKIP_ROWS, (GLint)unpackSkipRows); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, (GLint)unpackSkipPixels); + + if(genMipmaps && !GL_state.extensions.genMipmapSGIS) + { // Build all mipmap levels. + int neww, newh, bpp, w, h; + void* image, *newimage; + + bpp = BytesPerPixel(loadFormat); + if(bpp == 0) + throw Error("texturecontent_t::uploadTexture", "Unknown GL format " + String::number(loadFormat)); + + GL_OptimalTextureSize(width, height, false, true, &w, &h); + + if(w != width || h != height) + { + // Must rescale image to get "top" mipmap texture image. + image = GL_ScaleBufferEx(pixels, width, height, bpp, /*GL_UNSIGNED_BYTE,*/ + unpackRowLength, unpackAlignment, unpackSkipRows, unpackSkipPixels, + w, h, /*GL_UNSIGNED_BYTE,*/ packRowLength, packAlignment, packSkipRows, + packSkipPixels); + if(!image) + throw Error("texturecontent_t::uploadTexture", "Unknown error resizing mipmap level #0"); + } + else + { + image = (void*) pixels; + } + + for(;;) + { + glTexImage2D(GL_TEXTURE_2D, mipLevel, (GLint)glFormat, w, h, 0, (GLint)loadFormat, + GL_UNSIGNED_BYTE, image); + + if(w == 1 && h == 1) + break; + + ++mipLevel; + neww = (w < 2) ? 1 : w / 2; + newh = (h < 2) ? 1 : h / 2; + newimage = GL_ScaleBufferEx(image, w, h, bpp, /*GL_UNSIGNED_BYTE,*/ + unpackRowLength, unpackAlignment, unpackSkipRows, unpackSkipPixels, + neww, newh, /*GL_UNSIGNED_BYTE,*/ packRowLength, packAlignment, + packSkipRows, packSkipPixels); + if(!newimage) + throw Error("texturecontent_t::uploadTexture", "Unknown error resizing mipmap level #" + String::number(mipLevel)); + + if(image != pixels) + M_Free(image); + image = newimage; + + w = neww; + h = newh; + } + + if(image != pixels) + M_Free(image); + } + else + { + glTexImage2D(GL_TEXTURE_2D, mipLevel, (GLint)glFormat, (GLsizei)width, + (GLsizei)height, 0, (GLint)loadFormat, GL_UNSIGNED_BYTE, pixels); + } + + glPopClientAttrib(); + DENG_ASSERT(!Sys_GLCheckError()); + + return true; +} + +/** + * @param glFormat Identifier of the desired GL texture format. + * @param loadFormat Identifier of the GL texture format used during upload. + * @param pixels Texture pixel data to be uploaded. + * @param width Width of the texture in pixels. + * @param height Height of the texture in pixels. + * @param grayFactor Strength of the blend where @c 0:none @c 1:full. + * + * @return @c true iff successful. + */ +static boolean uploadTextureGrayMipmap(int glFormat, int loadFormat, const uint8_t* pixels, + int width, int height, float grayFactor) +{ + int i, w, h, numpels = width * height, numLevels, pixelSize; + uint8_t* image, *faded, *out; + const uint8_t* in; + float invFactor; + DENG_ASSERT(pixels); + + if(!(GL_RGB == loadFormat || GL_LUMINANCE == loadFormat)) + { + throw Error("texturecontent_t::uploadTextureGrayMipmap", "Unsupported load format " + String::number(loadFormat)); + } + + pixelSize = (loadFormat == GL_LUMINANCE? 1 : 3); + + // Can't operate on null texture. + if(width < 1 || height < 1) + return false; + + // Check that the texture dimensions are valid. + if(!GL_state.features.texNonPowTwo && + (width != M_CeilPow2(width) || height != M_CeilPow2(height))) + return false; + + if(width > GL_state.maxTexSize || height > GL_state.maxTexSize) + return false; + + numLevels = GL_NumMipmapLevels(width, height); + grayFactor = MINMAX_OF(0, grayFactor, 1); + invFactor = 1 - grayFactor; + + // Buffer used for the faded texture. + faded = (uint8_t*) M_Malloc(numpels / 4); + image = (uint8_t*) M_Malloc(numpels); + + // Initial fading. + in = pixels; + out = image; + for(i = 0; i < numpels; ++i) + { + *out++ = (uint8_t) MINMAX_OF(0, (*in * grayFactor + 127 * invFactor), 255); + in += pixelSize; + } + + // Upload the first level right away. + glTexImage2D(GL_TEXTURE_2D, 0, glFormat, width, height, 0, (GLint)loadFormat, + GL_UNSIGNED_BYTE, image); + + // Generate all mipmaps levels. + w = width; + h = height; + for(i = 0; i < numLevels; ++i) + { + GL_DownMipmap8(image, faded, w, h, (i * 1.75f) / numLevels); + + // Go down one level. + if(w > 1) + w /= 2; + if(h > 1) + h /= 2; + + glTexImage2D(GL_TEXTURE_2D, i + 1, glFormat, w, h, 0, (GLint)loadFormat, + GL_UNSIGNED_BYTE, faded); + } + + // Do we need to free the temp buffer? + M_Free(faded); + M_Free(image); + + DENG_ASSERT(!Sys_GLCheckError()); + return true; +} + /// @note Texture parameters will NOT be set here! void GL_UploadTextureContent(texturecontent_t const &content, gl::UploadMethod method) { @@ -626,8 +871,8 @@ void GL_UploadTextureContent(texturecontent_t const &content, gl::UploadMethod m GLint glFormat = ChooseTextureFormat(dglFormat, !noCompression); - if(!GL_UploadTexture(glFormat, loadFormat, loadPixels, loadWidth, loadHeight, - generateMipmaps ? true : false)) + if(!uploadTexture(glFormat, loadFormat, loadPixels, loadWidth, loadHeight, + generateMipmaps ? true : false)) { throw Error("GL_UploadTextureContent", QString("TexImage failed (%1:%2 fmt%3)") .arg(content.name) @@ -650,8 +895,8 @@ void GL_UploadTextureContent(texturecontent_t const &content, gl::UploadMethod m glFormat = ChooseTextureFormat(DGL_LUMINANCE, !noCompression); - if(!GL_UploadTextureGrayMipmap(glFormat, loadFormat, loadPixels, loadWidth, loadHeight, - content.grayMipmap * reciprocal255)) + if(!uploadTextureGrayMipmap(glFormat, loadFormat, loadPixels, loadWidth, loadHeight, + content.grayMipmap * reciprocal255)) { throw Error("GL_UploadTextureContent", QString("TexImageGrayMipmap failed (%1:%2 fmt%3)") .arg(content.name)