DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }