gl_Image.cpp (15819B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "../../idlib/precompiled.h" 30 31 /* 32 ================================================================================================ 33 Contains the Image implementation for OpenGL. 34 ================================================================================================ 35 */ 36 37 #include "../tr_local.h" 38 39 /* 40 ======================== 41 idImage::SubImageUpload 42 ======================== 43 */ 44 void idImage::SubImageUpload( int mipLevel, int x, int y, int z, int width, int height, const void * pic, int pixelPitch ) const { 45 assert( x >= 0 && y >= 0 && mipLevel >= 0 && width >= 0 && height >= 0 && mipLevel < opts.numLevels ); 46 47 int compressedSize = 0; 48 49 if ( IsCompressed() ) { 50 assert( !(x&3) && !(y&3) ); 51 52 // compressed size may be larger than the dimensions due to padding to quads 53 int quadW = ( width + 3 ) & ~3; 54 int quadH = ( height + 3 ) & ~3; 55 compressedSize = quadW * quadH * BitsForFormat( opts.format ) / 8; 56 57 int padW = ( opts.width + 3 ) & ~3; 58 int padH = ( opts.height + 3 ) & ~3; 59 (void)padH; 60 (void)padW; 61 assert( x + width <= padW && y + height <= padH ); 62 // upload the non-aligned value, OpenGL understands that there 63 // will be padding 64 if ( x + width > opts.width ) { 65 width = opts.width - x; 66 } 67 if ( y + height > opts.height ) { 68 height = opts.height - x; 69 } 70 } else { 71 assert( x + width <= opts.width && y + height <= opts.height ); 72 } 73 74 int target; 75 int uploadTarget; 76 if ( opts.textureType == TT_2D ) { 77 target = GL_TEXTURE_2D; 78 uploadTarget = GL_TEXTURE_2D; 79 } else if ( opts.textureType == TT_CUBIC ) { 80 target = GL_TEXTURE_CUBE_MAP_EXT; 81 uploadTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT + z; 82 } else { 83 assert( !"invalid opts.textureType" ); 84 target = GL_TEXTURE_2D; 85 uploadTarget = GL_TEXTURE_2D; 86 } 87 88 qglBindTexture( target, texnum ); 89 90 if ( pixelPitch != 0 ) { 91 qglPixelStorei( GL_UNPACK_ROW_LENGTH, pixelPitch ); 92 } 93 if ( opts.format == FMT_RGB565 ) { 94 glPixelStorei( GL_UNPACK_SWAP_BYTES, GL_TRUE ); 95 } 96 #ifdef DEBUG 97 GL_CheckErrors(); 98 #endif 99 if ( IsCompressed() ) { 100 qglCompressedTexSubImage2DARB( uploadTarget, mipLevel, x, y, width, height, internalFormat, compressedSize, pic ); 101 } else { 102 103 // make sure the pixel store alignment is correct so that lower mips get created 104 // properly for odd shaped textures - this fixes the mip mapping issues with 105 // fonts 106 int unpackAlignment = width * BitsForFormat( (textureFormat_t)opts.format ) / 8; 107 if ( ( unpackAlignment & 3 ) == 0 ) { 108 qglPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); 109 } else { 110 qglPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); 111 } 112 113 qglTexSubImage2D( uploadTarget, mipLevel, x, y, width, height, dataFormat, dataType, pic ); 114 } 115 #ifdef DEBUG 116 GL_CheckErrors(); 117 #endif 118 if ( opts.format == FMT_RGB565 ) { 119 glPixelStorei( GL_UNPACK_SWAP_BYTES, GL_FALSE ); 120 } 121 if ( pixelPitch != 0 ) { 122 qglPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); 123 } 124 } 125 126 /* 127 ======================== 128 idImage::SetPixel 129 ======================== 130 */ 131 void idImage::SetPixel( int mipLevel, int x, int y, const void * data, int dataSize ) { 132 SubImageUpload( mipLevel, x, y, 0, 1, 1, data ); 133 } 134 135 /* 136 ======================== 137 idImage::SetTexParameters 138 ======================== 139 */ 140 void idImage::SetTexParameters() { 141 int target = GL_TEXTURE_2D; 142 switch ( opts.textureType ) { 143 case TT_2D: 144 target = GL_TEXTURE_2D; 145 break; 146 case TT_CUBIC: 147 target = GL_TEXTURE_CUBE_MAP_EXT; 148 break; 149 default: 150 idLib::FatalError( "%s: bad texture type %d", GetName(), opts.textureType ); 151 return; 152 } 153 154 // ALPHA, LUMINANCE, LUMINANCE_ALPHA, and INTENSITY have been removed 155 // in OpenGL 3.2. In order to mimic those modes, we use the swizzle operators 156 #if defined( USE_CORE_PROFILE ) 157 if ( opts.colorFormat == CFM_GREEN_ALPHA ) { 158 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_ONE ); 159 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_ONE ); 160 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_ONE ); 161 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_GREEN ); 162 } else if ( opts.format == FMT_LUM8 ) { 163 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_RED ); 164 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_RED ); 165 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_RED ); 166 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_ONE ); 167 } else if ( opts.format == FMT_L8A8 ) { 168 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_RED ); 169 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_RED ); 170 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_RED ); 171 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_GREEN ); 172 } else if ( opts.format == FMT_ALPHA ) { 173 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_ONE ); 174 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_ONE ); 175 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_ONE ); 176 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_RED ); 177 } else if ( opts.format == FMT_INT8 ) { 178 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_RED ); 179 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_RED ); 180 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_RED ); 181 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_RED ); 182 } else { 183 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_RED ); 184 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_GREEN ); 185 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_BLUE ); 186 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA ); 187 } 188 #else 189 if ( opts.colorFormat == CFM_GREEN_ALPHA ) { 190 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_ONE ); 191 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_ONE ); 192 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_ONE ); 193 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_GREEN ); 194 } else if ( opts.format == FMT_ALPHA ) { 195 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_R, GL_ONE ); 196 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_G, GL_ONE ); 197 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_B, GL_ONE ); 198 qglTexParameteri( target, GL_TEXTURE_SWIZZLE_A, GL_RED ); 199 } 200 #endif 201 202 switch( filter ) { 203 case TF_DEFAULT: 204 if ( r_useTrilinearFiltering.GetBool() ) { 205 qglTexParameterf( target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); 206 } else { 207 qglTexParameterf( target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); 208 } 209 qglTexParameterf( target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 210 break; 211 case TF_LINEAR: 212 qglTexParameterf( target, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 213 qglTexParameterf( target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 214 break; 215 case TF_NEAREST: 216 qglTexParameterf( target, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); 217 qglTexParameterf( target, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); 218 break; 219 default: 220 common->FatalError( "%s: bad texture filter %d", GetName(), filter ); 221 } 222 223 if ( glConfig.anisotropicFilterAvailable ) { 224 // only do aniso filtering on mip mapped images 225 if ( filter == TF_DEFAULT ) { 226 int aniso = r_maxAnisotropicFiltering.GetInteger(); 227 if ( aniso > glConfig.maxTextureAnisotropy ) { 228 aniso = glConfig.maxTextureAnisotropy; 229 } 230 if ( aniso < 0 ) { 231 aniso = 0; 232 } 233 qglTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso ); 234 } else { 235 qglTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); 236 } 237 } 238 if ( glConfig.textureLODBiasAvailable && ( usage != TD_FONT ) ) { 239 // use a blurring LOD bias in combination with high anisotropy to fix our aliasing grate textures... 240 qglTexParameterf(target, GL_TEXTURE_LOD_BIAS_EXT, r_lodBias.GetFloat() ); 241 } 242 243 // set the wrap/clamp modes 244 switch( repeat ) { 245 case TR_REPEAT: 246 qglTexParameterf( target, GL_TEXTURE_WRAP_S, GL_REPEAT ); 247 qglTexParameterf( target, GL_TEXTURE_WRAP_T, GL_REPEAT ); 248 break; 249 case TR_CLAMP_TO_ZERO: { 250 float color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; 251 qglTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color ); 252 qglTexParameterf( target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); 253 qglTexParameterf( target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); 254 } 255 break; 256 case TR_CLAMP_TO_ZERO_ALPHA: { 257 float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; 258 qglTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color ); 259 qglTexParameterf( target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); 260 qglTexParameterf( target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); 261 } 262 break; 263 case TR_CLAMP: 264 qglTexParameterf( target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 265 qglTexParameterf( target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 266 break; 267 default: 268 common->FatalError( "%s: bad texture repeat %d", GetName(), repeat ); 269 } 270 } 271 272 /* 273 ======================== 274 idImage::AllocImage 275 276 Every image will pass through this function. Allocates all the necessary MipMap levels for the 277 Image, but doesn't put anything in them. 278 279 This should not be done during normal game-play, if you can avoid it. 280 ======================== 281 */ 282 void idImage::AllocImage() { 283 GL_CheckErrors(); 284 PurgeImage(); 285 286 switch ( opts.format ) { 287 case FMT_RGBA8: 288 internalFormat = GL_RGBA8; 289 dataFormat = GL_RGBA; 290 dataType = GL_UNSIGNED_BYTE; 291 break; 292 case FMT_XRGB8: 293 internalFormat = GL_RGB; 294 dataFormat = GL_RGBA; 295 dataType = GL_UNSIGNED_BYTE; 296 break; 297 case FMT_RGB565: 298 internalFormat = GL_RGB; 299 dataFormat = GL_RGB; 300 dataType = GL_UNSIGNED_SHORT_5_6_5; 301 break; 302 case FMT_ALPHA: 303 #if defined( USE_CORE_PROFILE ) 304 internalFormat = GL_R8; 305 dataFormat = GL_RED; 306 #else 307 internalFormat = GL_ALPHA8; 308 dataFormat = GL_ALPHA; 309 #endif 310 dataType = GL_UNSIGNED_BYTE; 311 break; 312 case FMT_L8A8: 313 #if defined( USE_CORE_PROFILE ) 314 internalFormat = GL_RG8; 315 dataFormat = GL_RG; 316 #else 317 internalFormat = GL_LUMINANCE8_ALPHA8; 318 dataFormat = GL_LUMINANCE_ALPHA; 319 #endif 320 dataType = GL_UNSIGNED_BYTE; 321 break; 322 case FMT_LUM8: 323 #if defined( USE_CORE_PROFILE ) 324 internalFormat = GL_R8; 325 dataFormat = GL_RED; 326 #else 327 internalFormat = GL_LUMINANCE8; 328 dataFormat = GL_LUMINANCE; 329 #endif 330 dataType = GL_UNSIGNED_BYTE; 331 break; 332 case FMT_INT8: 333 #if defined( USE_CORE_PROFILE ) 334 internalFormat = GL_R8; 335 dataFormat = GL_RED; 336 #else 337 internalFormat = GL_INTENSITY8; 338 dataFormat = GL_LUMINANCE; 339 #endif 340 dataType = GL_UNSIGNED_BYTE; 341 break; 342 case FMT_DXT1: 343 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; 344 dataFormat = GL_RGBA; 345 dataType = GL_UNSIGNED_BYTE; 346 break; 347 case FMT_DXT5: 348 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; 349 dataFormat = GL_RGBA; 350 dataType = GL_UNSIGNED_BYTE; 351 break; 352 case FMT_DEPTH: 353 internalFormat = GL_DEPTH_COMPONENT; 354 dataFormat = GL_DEPTH_COMPONENT; 355 dataType = GL_UNSIGNED_BYTE; 356 break; 357 case FMT_X16: 358 internalFormat = GL_INTENSITY16; 359 dataFormat = GL_LUMINANCE; 360 dataType = GL_UNSIGNED_SHORT; 361 break; 362 case FMT_Y16_X16: 363 internalFormat = GL_LUMINANCE16_ALPHA16; 364 dataFormat = GL_LUMINANCE_ALPHA; 365 dataType = GL_UNSIGNED_SHORT; 366 break; 367 default: 368 idLib::Error( "Unhandled image format %d in %s\n", opts.format, GetName() ); 369 } 370 371 // if we don't have a rendering context, just return after we 372 // have filled in the parms. We must have the values set, or 373 // an image match from a shader before OpenGL starts would miss 374 // the generated texture 375 if ( !R_IsInitialized() ) { 376 return; 377 } 378 379 // generate the texture number 380 qglGenTextures( 1, (GLuint *)&texnum ); 381 assert( texnum != TEXTURE_NOT_LOADED ); 382 383 //---------------------------------------------------- 384 // allocate all the mip levels with NULL data 385 //---------------------------------------------------- 386 387 int numSides; 388 int target; 389 int uploadTarget; 390 if ( opts.textureType == TT_2D ) { 391 target = uploadTarget = GL_TEXTURE_2D; 392 numSides = 1; 393 } else if ( opts.textureType == TT_CUBIC ) { 394 target = GL_TEXTURE_CUBE_MAP_EXT; 395 uploadTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT; 396 numSides = 6; 397 } else { 398 assert( !"opts.textureType" ); 399 target = uploadTarget = GL_TEXTURE_2D; 400 numSides = 1; 401 } 402 403 qglBindTexture( target, texnum ); 404 405 for ( int side = 0; side < numSides; side++ ) { 406 int w = opts.width; 407 int h = opts.height; 408 if ( opts.textureType == TT_CUBIC ) { 409 h = w; 410 } 411 for ( int level = 0; level < opts.numLevels; level++ ) { 412 413 // clear out any previous error 414 GL_CheckErrors(); 415 416 if ( IsCompressed() ) { 417 int compressedSize = ( ((w+3)/4) * ((h+3)/4) * int64( 16 ) * BitsForFormat( opts.format ) ) / 8; 418 419 // Even though the OpenGL specification allows the 'data' pointer to be NULL, for some 420 // drivers we actually need to upload data to get it to allocate the texture. 421 // However, on 32-bit systems we may fail to allocate a large block of memory for large 422 // textures. We handle this case by using HeapAlloc directly and allowing the allocation 423 // to fail in which case we simply pass down NULL to glCompressedTexImage2D and hope for the best. 424 // As of 2011-10-6 using NVIDIA hardware and drivers we have to allocate the memory with HeapAlloc 425 // with the exact size otherwise large image allocation (for instance for physical page textures) 426 // may fail on Vista 32-bit. 427 void * data = HeapAlloc( GetProcessHeap(), 0, compressedSize ); 428 qglCompressedTexImage2DARB( uploadTarget+side, level, internalFormat, w, h, 0, compressedSize, data ); 429 if ( data != NULL ) { 430 HeapFree( GetProcessHeap(), 0, data ); 431 } 432 } else { 433 qglTexImage2D( uploadTarget + side, level, internalFormat, w, h, 0, dataFormat, dataType, NULL ); 434 } 435 436 GL_CheckErrors(); 437 438 w = Max( 1, w >> 1 ); 439 h = Max( 1, h >> 1 ); 440 } 441 } 442 443 qglTexParameteri( target, GL_TEXTURE_MAX_LEVEL, opts.numLevels - 1 ); 444 445 // see if we messed anything up 446 GL_CheckErrors(); 447 448 SetTexParameters(); 449 450 GL_CheckErrors(); 451 } 452 453 /* 454 ======================== 455 idImage::PurgeImage 456 ======================== 457 */ 458 void idImage::PurgeImage() { 459 if ( texnum != TEXTURE_NOT_LOADED ) { 460 qglDeleteTextures( 1, (GLuint *)&texnum ); // this should be the ONLY place it is ever called! 461 texnum = TEXTURE_NOT_LOADED; 462 } 463 // clear all the current binding caches, so the next bind will do a real one 464 for ( int i = 0 ; i < MAX_MULTITEXTURE_UNITS ; i++ ) { 465 backEnd.glState.tmu[i].current2DMap = TEXTURE_NOT_LOADED; 466 backEnd.glState.tmu[i].currentCubeMap = TEXTURE_NOT_LOADED; 467 } 468 } 469 470 /* 471 ======================== 472 idImage::Resize 473 ======================== 474 */ 475 void idImage::Resize( int width, int height ) { 476 if ( opts.width == width && opts.height == height ) { 477 return; 478 } 479 opts.width = width; 480 opts.height = height; 481 AllocImage(); 482 }