Image_load.cpp (19199B)
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 29 #pragma hdrstop 30 #include "../idlib/precompiled.h" 31 32 #include "tr_local.h" 33 34 /* 35 ================ 36 BitsForFormat 37 ================ 38 */ 39 int BitsForFormat( textureFormat_t format ) { 40 switch ( format ) { 41 case FMT_NONE: return 0; 42 case FMT_RGBA8: return 32; 43 case FMT_XRGB8: return 32; 44 case FMT_RGB565: return 16; 45 case FMT_L8A8: return 16; 46 case FMT_ALPHA: return 8; 47 case FMT_LUM8: return 8; 48 case FMT_INT8: return 8; 49 case FMT_DXT1: return 4; 50 case FMT_DXT5: return 8; 51 case FMT_DEPTH: return 32; 52 case FMT_X16: return 16; 53 case FMT_Y16_X16: return 32; 54 default: 55 assert( 0 ); 56 return 0; 57 } 58 } 59 60 /* 61 ======================== 62 idImage::DeriveOpts 63 ======================== 64 */ 65 ID_INLINE void idImage::DeriveOpts() { 66 67 if ( opts.format == FMT_NONE ) { 68 opts.colorFormat = CFM_DEFAULT; 69 70 switch ( usage ) { 71 case TD_COVERAGE: 72 opts.format = FMT_DXT1; 73 opts.colorFormat = CFM_GREEN_ALPHA; 74 break; 75 case TD_DEPTH: 76 opts.format = FMT_DEPTH; 77 break; 78 case TD_DIFFUSE: 79 // TD_DIFFUSE gets only set to when its a diffuse texture for an interaction 80 opts.gammaMips = true; 81 opts.format = FMT_DXT5; 82 opts.colorFormat = CFM_YCOCG_DXT5; 83 break; 84 case TD_SPECULAR: 85 opts.gammaMips = true; 86 opts.format = FMT_DXT1; 87 opts.colorFormat = CFM_DEFAULT; 88 break; 89 case TD_DEFAULT: 90 opts.gammaMips = true; 91 opts.format = FMT_DXT5; 92 opts.colorFormat = CFM_DEFAULT; 93 break; 94 case TD_BUMP: 95 opts.format = FMT_DXT5; 96 opts.colorFormat = CFM_NORMAL_DXT5; 97 break; 98 case TD_FONT: 99 opts.format = FMT_DXT1; 100 opts.colorFormat = CFM_GREEN_ALPHA; 101 opts.numLevels = 4; // We only support 4 levels because we align to 16 in the exporter 102 opts.gammaMips = true; 103 break; 104 case TD_LIGHT: 105 opts.format = FMT_RGB565; 106 opts.gammaMips = true; 107 break; 108 case TD_LOOKUP_TABLE_MONO: 109 opts.format = FMT_INT8; 110 break; 111 case TD_LOOKUP_TABLE_ALPHA: 112 opts.format = FMT_ALPHA; 113 break; 114 case TD_LOOKUP_TABLE_RGB1: 115 case TD_LOOKUP_TABLE_RGBA: 116 opts.format = FMT_RGBA8; 117 break; 118 default: 119 assert( false ); 120 opts.format = FMT_RGBA8; 121 } 122 } 123 124 if ( opts.numLevels == 0 ) { 125 opts.numLevels = 1; 126 127 if ( filter == TF_LINEAR || filter == TF_NEAREST ) { 128 // don't create mip maps if we aren't going to be using them 129 } else { 130 int temp_width = opts.width; 131 int temp_height = opts.height; 132 while ( temp_width > 1 || temp_height > 1 ) { 133 temp_width >>= 1; 134 temp_height >>= 1; 135 if ( ( opts.format == FMT_DXT1 || opts.format == FMT_DXT5 ) && 136 ( ( temp_width & 0x3 ) != 0 || ( temp_height & 0x3 ) != 0 ) ) { 137 break; 138 } 139 opts.numLevels++; 140 } 141 } 142 } 143 } 144 145 /* 146 ======================== 147 idImage::AllocImage 148 ======================== 149 */ 150 void idImage::AllocImage( const idImageOpts &imgOpts, textureFilter_t tf, textureRepeat_t tr ) { 151 filter = tf; 152 repeat = tr; 153 opts = imgOpts; 154 DeriveOpts(); 155 AllocImage(); 156 } 157 158 /* 159 ================ 160 GenerateImage 161 ================ 162 */ 163 void idImage::GenerateImage( const byte *pic, int width, int height, textureFilter_t filterParm, textureRepeat_t repeatParm, textureUsage_t usageParm ) { 164 PurgeImage(); 165 166 filter = filterParm; 167 repeat = repeatParm; 168 usage = usageParm; 169 cubeFiles = CF_2D; 170 171 opts.textureType = TT_2D; 172 opts.width = width; 173 opts.height = height; 174 opts.numLevels = 0; 175 DeriveOpts(); 176 177 // if we don't have a rendering context, just return after we 178 // have filled in the parms. We must have the values set, or 179 // an image match from a shader before the render starts would miss 180 // the generated texture 181 if ( !R_IsInitialized() ) { 182 return; 183 } 184 185 idBinaryImage im( GetName() ); 186 im.Load2DFromMemory( width, height, pic, opts.numLevels, opts.format, opts.colorFormat, opts.gammaMips ); 187 188 AllocImage(); 189 190 for ( int i = 0; i < im.NumImages(); i++ ) { 191 const bimageImage_t & img = im.GetImageHeader( i ); 192 const byte * data = im.GetImageData( i ); 193 SubImageUpload( img.level, 0, 0, img.destZ, img.width, img.height, data ); 194 } 195 } 196 197 /* 198 ==================== 199 GenerateCubeImage 200 201 Non-square cube sides are not allowed 202 ==================== 203 */ 204 void idImage::GenerateCubeImage( const byte *pic[6], int size, textureFilter_t filterParm, textureUsage_t usageParm ) { 205 PurgeImage(); 206 207 filter = filterParm; 208 repeat = TR_CLAMP; 209 usage = usageParm; 210 cubeFiles = CF_NATIVE; 211 212 opts.textureType = TT_CUBIC; 213 opts.width = size; 214 opts.height = size; 215 opts.numLevels = 0; 216 DeriveOpts(); 217 218 // if we don't have a rendering context, just return after we 219 // have filled in the parms. We must have the values set, or 220 // an image match from a shader before the render starts would miss 221 // the generated texture 222 if ( !R_IsInitialized() ) { 223 return; 224 } 225 226 idBinaryImage im( GetName() ); 227 im.LoadCubeFromMemory( size, pic, opts.numLevels, opts.format, opts.gammaMips ); 228 229 AllocImage(); 230 231 for ( int i = 0; i < im.NumImages(); i++ ) { 232 const bimageImage_t & img = im.GetImageHeader( i ); 233 const byte * data = im.GetImageData( i ); 234 SubImageUpload( img.level, 0, 0, img.destZ, img.width, img.height, data ); 235 } 236 } 237 238 /* 239 =============== 240 GetGeneratedName 241 242 name contains GetName() upon entry 243 =============== 244 */ 245 void idImage::GetGeneratedName( idStr &_name, const textureUsage_t &_usage, const cubeFiles_t &_cube ) { 246 idStrStatic< 64 > extension; 247 248 _name.ExtractFileExtension( extension ); 249 _name.StripFileExtension(); 250 251 _name += va( "#__%02d%02d", (int)_usage, (int)_cube ); 252 if ( extension.Length() > 0 ) { 253 _name.SetFileExtension( extension ); 254 } 255 } 256 257 258 /* 259 =============== 260 ActuallyLoadImage 261 262 Absolutely every image goes through this path 263 On exit, the idImage will have a valid OpenGL texture number that can be bound 264 =============== 265 */ 266 void idImage::ActuallyLoadImage( bool fromBackEnd ) { 267 268 // if we don't have a rendering context yet, just return 269 if ( !R_IsInitialized() ) { 270 return; 271 } 272 273 // this is the ONLY place generatorFunction will ever be called 274 if ( generatorFunction ) { 275 generatorFunction( this ); 276 return; 277 } 278 279 if ( com_productionMode.GetInteger() != 0 ) { 280 sourceFileTime = FILE_NOT_FOUND_TIMESTAMP; 281 if ( cubeFiles != CF_2D ) { 282 opts.textureType = TT_CUBIC; 283 repeat = TR_CLAMP; 284 } 285 } else { 286 if ( cubeFiles != CF_2D ) { 287 opts.textureType = TT_CUBIC; 288 repeat = TR_CLAMP; 289 R_LoadCubeImages( GetName(), cubeFiles, NULL, NULL, &sourceFileTime ); 290 } else { 291 opts.textureType = TT_2D; 292 R_LoadImageProgram( GetName(), NULL, NULL, NULL, &sourceFileTime, &usage ); 293 } 294 } 295 296 // Figure out opts.colorFormat and opts.format so we can make sure the binary image is up to date 297 DeriveOpts(); 298 299 idStrStatic< MAX_OSPATH > generatedName = GetName(); 300 GetGeneratedName( generatedName, usage, cubeFiles ); 301 302 idBinaryImage im( generatedName ); 303 binaryFileTime = im.LoadFromGeneratedFile( sourceFileTime ); 304 305 // BFHACK, do not want to tweak on buildgame so catch these images here 306 if ( binaryFileTime == FILE_NOT_FOUND_TIMESTAMP && fileSystem->UsingResourceFiles() ) { 307 int c = 1; 308 while ( c-- > 0 ) { 309 if ( generatedName.Find( "guis/assets/white#__0000", false ) >= 0 ) { 310 generatedName.Replace( "white#__0000", "white#__0200" ); 311 im.SetName( generatedName ); 312 binaryFileTime = im.LoadFromGeneratedFile( sourceFileTime ); 313 break; 314 } 315 if ( generatedName.Find( "guis/assets/white#__0100", false ) >= 0 ) { 316 generatedName.Replace( "white#__0100", "white#__0200" ); 317 im.SetName( generatedName ); 318 binaryFileTime = im.LoadFromGeneratedFile( sourceFileTime ); 319 break; 320 } 321 if ( generatedName.Find( "textures/black#__0100", false ) >= 0 ) { 322 generatedName.Replace( "black#__0100", "black#__0200" ); 323 im.SetName( generatedName ); 324 binaryFileTime = im.LoadFromGeneratedFile( sourceFileTime ); 325 break; 326 } 327 if ( generatedName.Find( "textures/decals/bulletglass1_d#__0100", false ) >= 0 ) { 328 generatedName.Replace( "bulletglass1_d#__0100", "bulletglass1_d#__0200" ); 329 im.SetName( generatedName ); 330 binaryFileTime = im.LoadFromGeneratedFile( sourceFileTime ); 331 break; 332 } 333 if ( generatedName.Find( "models/monsters/skeleton/skeleton01_d#__1000", false ) >= 0 ) { 334 generatedName.Replace( "skeleton01_d#__1000", "skeleton01_d#__0100" ); 335 im.SetName( generatedName ); 336 binaryFileTime = im.LoadFromGeneratedFile( sourceFileTime ); 337 break; 338 } 339 } 340 } 341 const bimageFile_t & header = im.GetFileHeader(); 342 343 if ( ( fileSystem->InProductionMode() && binaryFileTime != FILE_NOT_FOUND_TIMESTAMP ) || ( ( binaryFileTime != FILE_NOT_FOUND_TIMESTAMP ) 344 && ( header.colorFormat == opts.colorFormat ) 345 && ( header.format == opts.format ) 346 && ( header.textureType == opts.textureType ) 347 ) ) { 348 opts.width = header.width; 349 opts.height = header.height; 350 opts.numLevels = header.numLevels; 351 opts.colorFormat = (textureColor_t)header.colorFormat; 352 opts.format = (textureFormat_t)header.format; 353 opts.textureType = (textureType_t)header.textureType; 354 if ( cvarSystem->GetCVarBool( "fs_buildresources" ) ) { 355 // for resource gathering write this image to the preload file for this map 356 fileSystem->AddImagePreload( GetName(), filter, repeat, usage, cubeFiles ); 357 } 358 } else { 359 if ( cubeFiles != CF_2D ) { 360 int size; 361 byte * pics[6]; 362 363 if ( !R_LoadCubeImages( GetName(), cubeFiles, pics, &size, &sourceFileTime ) || size == 0 ) { 364 idLib::Warning( "Couldn't load cube image: %s", GetName() ); 365 return; 366 } 367 368 opts.textureType = TT_CUBIC; 369 repeat = TR_CLAMP; 370 opts.width = size; 371 opts.height = size; 372 opts.numLevels = 0; 373 DeriveOpts(); 374 im.LoadCubeFromMemory( size, (const byte **)pics, opts.numLevels, opts.format, opts.gammaMips ); 375 repeat = TR_CLAMP; 376 377 for ( int i = 0; i < 6; i++ ) { 378 if ( pics[i] ) { 379 Mem_Free( pics[i] ); 380 } 381 } 382 } else { 383 int width, height; 384 byte * pic; 385 386 // load the full specification, and perform any image program calculations 387 R_LoadImageProgram( GetName(), &pic, &width, &height, &sourceFileTime, &usage ); 388 389 if ( pic == NULL ) { 390 idLib::Warning( "Couldn't load image: %s : %s", GetName(), generatedName.c_str() ); 391 // create a default so it doesn't get continuously reloaded 392 opts.width = 8; 393 opts.height = 8; 394 opts.numLevels = 1; 395 DeriveOpts(); 396 AllocImage(); 397 398 // clear the data so it's not left uninitialized 399 idTempArray<byte> clear( opts.width * opts.height * 4 ); 400 memset( clear.Ptr(), 0, clear.Size() ); 401 for ( int level = 0; level < opts.numLevels; level++ ) { 402 SubImageUpload( level, 0, 0, 0, opts.width >> level, opts.height >> level, clear.Ptr() ); 403 } 404 405 return; 406 } 407 408 opts.width = width; 409 opts.height = height; 410 opts.numLevels = 0; 411 DeriveOpts(); 412 im.Load2DFromMemory( opts.width, opts.height, pic, opts.numLevels, opts.format, opts.colorFormat, opts.gammaMips ); 413 414 Mem_Free( pic ); 415 } 416 binaryFileTime = im.WriteGeneratedFile( sourceFileTime ); 417 } 418 419 AllocImage(); 420 421 422 for ( int i = 0; i < im.NumImages(); i++ ) { 423 const bimageImage_t & img = im.GetImageHeader( i ); 424 const byte * data = im.GetImageData( i ); 425 SubImageUpload( img.level, 0, 0, img.destZ, img.width, img.height, data ); 426 } 427 } 428 429 /* 430 ============== 431 Bind 432 433 Automatically enables 2D mapping or cube mapping if needed 434 ============== 435 */ 436 void idImage::Bind() { 437 438 RENDERLOG_PRINTF( "idImage::Bind( %s )\n", GetName() ); 439 440 // load the image if necessary (FIXME: not SMP safe!) 441 if ( !IsLoaded() ) { 442 // load the image on demand here, which isn't our normal game operating mode 443 ActuallyLoadImage( true ); 444 } 445 446 const int texUnit = backEnd.glState.currenttmu; 447 448 tmu_t * tmu = &backEnd.glState.tmu[texUnit]; 449 // bind the texture 450 if ( opts.textureType == TT_2D ) { 451 if ( tmu->current2DMap != texnum ) { 452 tmu->current2DMap = texnum; 453 qglBindMultiTextureEXT( GL_TEXTURE0_ARB + texUnit, GL_TEXTURE_2D, texnum ); 454 } 455 } else if ( opts.textureType == TT_CUBIC ) { 456 if ( tmu->currentCubeMap != texnum ) { 457 tmu->currentCubeMap = texnum; 458 qglBindMultiTextureEXT( GL_TEXTURE0_ARB + texUnit, GL_TEXTURE_CUBE_MAP_EXT, texnum ); 459 } 460 } 461 462 } 463 464 /* 465 ================ 466 MakePowerOfTwo 467 ================ 468 */ 469 int MakePowerOfTwo( int num ) { 470 int pot; 471 for ( pot = 1; pot < num; pot <<= 1 ) { 472 } 473 return pot; 474 } 475 476 /* 477 ==================== 478 CopyFramebuffer 479 ==================== 480 */ 481 void idImage::CopyFramebuffer( int x, int y, int imageWidth, int imageHeight ) { 482 483 484 qglBindTexture( ( opts.textureType == TT_CUBIC ) ? GL_TEXTURE_CUBE_MAP_EXT : GL_TEXTURE_2D, texnum ); 485 486 qglReadBuffer( GL_BACK ); 487 488 opts.width = imageWidth; 489 opts.height = imageHeight; 490 qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, x, y, imageWidth, imageHeight, 0 ); 491 492 // these shouldn't be necessary if the image was initialized properly 493 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 494 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 495 496 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 497 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 498 499 backEnd.pc.c_copyFrameBuffer++; 500 } 501 502 /* 503 ==================== 504 CopyDepthbuffer 505 ==================== 506 */ 507 void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight ) { 508 qglBindTexture( ( opts.textureType == TT_CUBIC ) ? GL_TEXTURE_CUBE_MAP_EXT : GL_TEXTURE_2D, texnum ); 509 510 opts.width = imageWidth; 511 opts.height = imageHeight; 512 qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, 0 ); 513 514 backEnd.pc.c_copyFrameBuffer++; 515 } 516 517 /* 518 ============= 519 RB_UploadScratchImage 520 521 if rows = cols * 6, assume it is a cube map animation 522 ============= 523 */ 524 void idImage::UploadScratch( const byte * data, int cols, int rows ) { 525 526 // if rows = cols * 6, assume it is a cube map animation 527 if ( rows == cols * 6 ) { 528 rows /= 6; 529 const byte * pic[6]; 530 for ( int i = 0; i < 6; i++ ) { 531 pic[i] = data + cols * rows * 4 * i; 532 } 533 534 if ( opts.textureType != TT_CUBIC || usage != TD_LOOKUP_TABLE_RGBA ) { 535 GenerateCubeImage( pic, cols, TF_LINEAR, TD_LOOKUP_TABLE_RGBA ); 536 return; 537 } 538 if ( opts.width != cols || opts.height != rows ) { 539 opts.width = cols; 540 opts.height = rows; 541 AllocImage(); 542 } 543 SetSamplerState( TF_LINEAR, TR_CLAMP ); 544 for ( int i = 0; i < 6; i++ ) { 545 SubImageUpload( 0, 0, 0, i, opts.width, opts.height, pic[i] ); 546 } 547 548 } else { 549 if ( opts.textureType != TT_2D || usage != TD_LOOKUP_TABLE_RGBA ) { 550 GenerateImage( data, cols, rows, TF_LINEAR, TR_REPEAT, TD_LOOKUP_TABLE_RGBA ); 551 return; 552 } 553 if ( opts.width != cols || opts.height != rows ) { 554 opts.width = cols; 555 opts.height = rows; 556 AllocImage(); 557 } 558 SetSamplerState( TF_LINEAR, TR_REPEAT ); 559 SubImageUpload( 0, 0, 0, 0, opts.width, opts.height, data ); 560 } 561 } 562 563 /* 564 ================== 565 StorageSize 566 ================== 567 */ 568 int idImage::StorageSize() const { 569 570 if ( !IsLoaded() ) { 571 return 0; 572 } 573 int baseSize = opts.width * opts.height; 574 if ( opts.numLevels > 1 ) { 575 baseSize *= 4; 576 baseSize /= 3; 577 } 578 baseSize *= BitsForFormat( opts.format ); 579 baseSize /= 8; 580 return baseSize; 581 } 582 583 /* 584 ================== 585 Print 586 ================== 587 */ 588 void idImage::Print() const { 589 if ( generatorFunction ) { 590 common->Printf( "F" ); 591 } else { 592 common->Printf( " " ); 593 } 594 595 switch ( opts.textureType ) { 596 case TT_2D: 597 common->Printf( " " ); 598 break; 599 case TT_CUBIC: 600 common->Printf( "C" ); 601 break; 602 default: 603 common->Printf( "<BAD TYPE:%i>", opts.textureType ); 604 break; 605 } 606 607 common->Printf( "%4i %4i ", opts.width, opts.height ); 608 609 switch ( opts.format ) { 610 #define NAME_FORMAT( x ) case FMT_##x: common->Printf( "%-6s ", #x ); break; 611 NAME_FORMAT( NONE ); 612 NAME_FORMAT( RGBA8 ); 613 NAME_FORMAT( XRGB8 ); 614 NAME_FORMAT( RGB565 ); 615 NAME_FORMAT( L8A8 ); 616 NAME_FORMAT( ALPHA ); 617 NAME_FORMAT( LUM8 ); 618 NAME_FORMAT( INT8 ); 619 NAME_FORMAT( DXT1 ); 620 NAME_FORMAT( DXT5 ); 621 NAME_FORMAT( DEPTH ); 622 NAME_FORMAT( X16 ); 623 NAME_FORMAT( Y16_X16 ); 624 default: 625 common->Printf( "<%3i>", opts.format ); 626 break; 627 } 628 629 switch( filter ) { 630 case TF_DEFAULT: 631 common->Printf( "mip " ); 632 break; 633 case TF_LINEAR: 634 common->Printf( "linr " ); 635 break; 636 case TF_NEAREST: 637 common->Printf( "nrst " ); 638 break; 639 default: 640 common->Printf( "<BAD FILTER:%i>", filter ); 641 break; 642 } 643 644 switch ( repeat ) { 645 case TR_REPEAT: 646 common->Printf( "rept " ); 647 break; 648 case TR_CLAMP_TO_ZERO: 649 common->Printf( "zero " ); 650 break; 651 case TR_CLAMP_TO_ZERO_ALPHA: 652 common->Printf( "azro " ); 653 break; 654 case TR_CLAMP: 655 common->Printf( "clmp " ); 656 break; 657 default: 658 common->Printf( "<BAD REPEAT:%i>", repeat ); 659 break; 660 } 661 662 common->Printf( "%4ik ", StorageSize() / 1024 ); 663 664 common->Printf( " %s\n", GetName() ); 665 } 666 667 /* 668 =============== 669 idImage::Reload 670 =============== 671 */ 672 void idImage::Reload( bool force ) { 673 // always regenerate functional images 674 if ( generatorFunction ) { 675 common->DPrintf( "regenerating %s.\n", GetName() ); 676 generatorFunction( this ); 677 return; 678 } 679 680 // check file times 681 if ( !force ) { 682 ID_TIME_T current; 683 if ( cubeFiles != CF_2D ) { 684 R_LoadCubeImages( imgName, cubeFiles, NULL, NULL, ¤t ); 685 } else { 686 // get the current values 687 R_LoadImageProgram( imgName, NULL, NULL, NULL, ¤t ); 688 } 689 if ( current <= sourceFileTime ) { 690 return; 691 } 692 } 693 694 common->DPrintf( "reloading %s.\n", GetName() ); 695 696 PurgeImage(); 697 698 // Load is from the front end, so the back end must be synced 699 ActuallyLoadImage( false ); 700 } 701 702 /* 703 ======================== 704 idImage::SetSamplerState 705 ======================== 706 */ 707 void idImage::SetSamplerState( textureFilter_t tf, textureRepeat_t tr ) { 708 if ( tf == filter && tr == repeat ) { 709 return; 710 } 711 filter = tf; 712 repeat = tr; 713 qglBindTexture( ( opts.textureType == TT_CUBIC ) ? GL_TEXTURE_CUBE_MAP_EXT : GL_TEXTURE_2D, texnum ); 714 SetTexParameters(); 715 }