/* ** gltexture.cpp ** Low level OpenGL texture handling. These classes are also ** containers for the various translations a texture can have. ** **--------------------------------------------------------------------------- ** Copyright 2004-2005 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be ** covered by the terms of the GNU Lesser General Public License as published ** by the Free Software Foundation; either version 2.1 of the License, or (at ** your option) any later version. ** 5. Full disclosure of the entire project's source code, except for third ** party libraries is mandatory. (NOTE: This clause is non-negotiable!) ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "gl/system/gl_system.h" #include "templates.h" #include "m_crc32.h" #include "c_cvars.h" #include "c_dispatch.h" #include "v_palette.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_cvars.h" #include "gl/renderer/gl_renderer.h" #include "gl/textures/gl_material.h" extern TexFilter_s TexFilter[]; extern int TexFormat[]; //=========================================================================== // // Static texture data // //=========================================================================== unsigned int FHardwareTexture::lastbound[FHardwareTexture::MAX_TEXTURES]; //=========================================================================== // // STATIC - Gets the maximum size of hardware textures // //=========================================================================== int FHardwareTexture::GetTexDimension(int value) { if (value > gl.max_texturesize) return gl.max_texturesize; return value; } //=========================================================================== // // Quick'n dirty image rescaling. // // This will only be used when the source texture is larger than // what the hardware can manage (extremely rare in Doom) // // Code taken from wxWidgets // //=========================================================================== struct BoxPrecalc { int boxStart; int boxEnd; }; static void ResampleBoxPrecalc(TArray& boxes, int oldDim) { int newDim = boxes.Size(); const double scale_factor_1 = double(oldDim) / newDim; const int scale_factor_2 = (int)(scale_factor_1 / 2); for (int dst = 0; dst < newDim; ++dst) { // Source pixel in the Y direction const int src_p = int(dst * scale_factor_1); BoxPrecalc& precalc = boxes[dst]; precalc.boxStart = clamp(int(src_p - scale_factor_1 / 2.0 + 1), 0, oldDim - 1); precalc.boxEnd = clamp(MAX(precalc.boxStart + 1, int(src_p + scale_factor_2)), 0, oldDim - 1); } } void FHardwareTexture::Resize(int width, int height, unsigned char *src_data, unsigned char *dst_data) { // This function implements a simple pre-blur/box averaging method for // downsampling that gives reasonably smooth results To scale the image // down we will need to gather a grid of pixels of the size of the scale // factor in each direction and then do an averaging of the pixels. TArray vPrecalcs(height); TArray hPrecalcs(width); ResampleBoxPrecalc(vPrecalcs, texheight); ResampleBoxPrecalc(hPrecalcs, texwidth); int averaged_pixels, averaged_alpha, src_pixel_index; double sum_r, sum_g, sum_b, sum_a; for (int y = 0; y < height; y++) // Destination image - Y direction { // Source pixel in the Y direction const BoxPrecalc& vPrecalc = vPrecalcs[y]; for (int x = 0; x < width; x++) // Destination image - X direction { // Source pixel in the X direction const BoxPrecalc& hPrecalc = hPrecalcs[x]; // Box of pixels to average averaged_pixels = 0; averaged_alpha = 0; sum_r = sum_g = sum_b = sum_a = 0.0; for (int j = vPrecalc.boxStart; j <= vPrecalc.boxEnd; ++j) { for (int i = hPrecalc.boxStart; i <= hPrecalc.boxEnd; ++i) { // Calculate the actual index in our source pixels src_pixel_index = j * texwidth + i; int a = src_data[src_pixel_index * 4 + 3]; if (a > 0) // do not use color from fully transparent pixels { sum_r += src_data[src_pixel_index * 4 + 0]; sum_g += src_data[src_pixel_index * 4 + 1]; sum_b += src_data[src_pixel_index * 4 + 2]; sum_a += a; averaged_pixels++; } averaged_alpha++; } } // Calculate the average from the sum and number of averaged pixels dst_data[0] = (unsigned char)xs_CRoundToInt(sum_r / averaged_pixels); dst_data[1] = (unsigned char)xs_CRoundToInt(sum_g / averaged_pixels); dst_data[2] = (unsigned char)xs_CRoundToInt(sum_b / averaged_pixels); dst_data[3] = (unsigned char)xs_CRoundToInt(sum_a / averaged_alpha); dst_data += 4; } } } //=========================================================================== // // Loads the texture image into the hardware // // NOTE: For some strange reason I was unable to find the source buffer // should be one line higher than the actual texture. I got extremely // strange crashes deep inside the GL driver when I didn't do it! // //=========================================================================== void FHardwareTexture::LoadImage(unsigned char * buffer,int w, int h, unsigned int & glTexID,int wrapparam, bool alphatexture, int texunit) { int rh,rw; int texformat=TexFormat[gl_texture_format]; bool deletebuffer=false; bool use_mipmapping = TexFilter[gl_texture_filter].mipmapping; if (alphatexture) { texformat = GL_R8; } else if (forcenocompression) { texformat = GL_RGBA8; } if (glTexID==0) glGenTextures(1,&glTexID); glBindTexture(GL_TEXTURE_2D, glTexID); lastbound[texunit]=glTexID; if (!buffer) { w=texwidth; h=abs(texheight); rw = GetTexDimension (w); rh = GetTexDimension (h); // The texture must at least be initialized if no data is present. mipmap=false; buffer=(unsigned char *)calloc(4,rw * (rh+1)); deletebuffer=true; //texheight=-h; } else { rw = GetTexDimension (w); rh = GetTexDimension (h); if (rw < w || rh < h) { // The texture is larger than what the hardware can handle so scale it down. unsigned char * scaledbuffer=(unsigned char *)calloc(4,rw * (rh+1)); if (scaledbuffer) { Resize(rw, rh, buffer, scaledbuffer); deletebuffer=true; buffer=scaledbuffer; } } } glTexImage2D(GL_TEXTURE_2D, 0, texformat, rw, rh, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); if (deletebuffer) free(buffer); if (mipmap && use_mipmapping && !forcenofiltering) glGenerateMipmap(GL_TEXTURE_2D); if (alphatexture) { static const GLint swizzleMask[] = {GL_ONE, GL_ONE, GL_ONE, GL_RED}; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); } // When using separate samplers the stuff below is not needed. // if (gl.flags & RFL_SAMPLER_OBJECTS) return; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapparam); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapparam); clampmode = wrapparam==GL_CLAMP_TO_EDGE? GLT_CLAMPX|GLT_CLAMPY : 0; if (forcenofiltering) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f); } else { if (mipmap && use_mipmapping) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[gl_texture_filter].minfilter); if (gl_texture_filter_anisotropic) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_filter_anisotropic); } } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[gl_texture_filter].magfilter); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[gl_texture_filter].magfilter); } } //=========================================================================== // // Creates a texture // //=========================================================================== FHardwareTexture::FHardwareTexture(int _width, int _height, bool _mipmap, bool wrap, bool nofilter, bool nocompression) { forcenocompression = nocompression; mipmap=_mipmap; texwidth=_width; texheight=_height; glDefTexID = 0; clampmode=0; glDepthID = 0; forcenofiltering = nofilter; } //=========================================================================== // // Deletes a texture id and unbinds it from the texture units // //=========================================================================== void FHardwareTexture::DeleteTexture(unsigned int texid) { if (texid != 0) { for(int i = 0; i < MAX_TEXTURES; i++) { if (lastbound[i] == texid) { lastbound[i] = 0; } } glDeleteTextures(1, &texid); } } //=========================================================================== // // Frees all associated resources // //=========================================================================== void FHardwareTexture::Clean(bool all) { int cm_arraysize = CM_FIRSTSPECIALCOLORMAP + SpecialColormaps.Size(); if (all) { DeleteTexture(glDefTexID); glDefTexID = 0; } for(unsigned int i=0;i