2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
35 PROBLEM: compressed textures may break the zero clamp rule!
38 static bool FormatIsDXT( int internalFormat ) {
39 if ( internalFormat < GL_COMPRESSED_RGB_S3TC_DXT1_EXT
40 || internalFormat > GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ) {
46 int MakePowerOfTwo( int num ) {
48 for (pot = 1 ; pot < num ; pot<<=1) {
57 Used for determining memory utilization
60 int idImage::BitsForInternalFormat( int internalFormat ) const {
61 switch ( internalFormat ) {
66 case GL_LUMINANCE8_ALPHA8:
69 return 32; // on some future hardware, this may actually be 24, but be conservative
79 return 32; // on some future hardware, this may actually be 24, but be conservative
80 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
82 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
84 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
86 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
92 case GL_COLOR_INDEX8_EXT:
96 case GL_COMPRESSED_RGB_ARB:
98 case GL_COMPRESSED_RGBA_ARB:
101 common->Error( "R_BitsForInternalFormat: BAD FORMAT:%i", internalFormat );
108 UploadCompressedNormalMap
110 Create a 256 color palette to be used by compressed normal maps
113 void idImage::UploadCompressedNormalMap( int width, int height, const byte *rgba, int mipLevel ) {
121 // OpenGL's pixel packing rule
122 row = width < 4 ? 4 : width;
124 normals = (byte *)_alloca( row * height );
126 common->Error( "R_UploadCompressedNormalMap: _alloca failed" );
131 for ( i = 0 ; i < height ; i++, out += row, in += width * 4 ) {
132 for ( j = 0 ; j < width ; j++ ) {
138 if ( x == 128 && y == 128 && z == 128 ) {
139 // the "nullnormal" color
142 c = ( globalImages->originalToCompressed[x] << 4 ) | globalImages->originalToCompressed[y];
144 c = 254; // don't use the nullnormal color
151 if ( mipLevel == 0 ) {
152 // Optionally write out the paletized normal map to a .tga
153 if ( globalImages->image_writeNormalTGAPalletized.GetBool() ) {
154 char filename[MAX_IMAGE_NAME];
155 ImageProgramStringToCompressedFileName( imgName, filename );
156 char *ext = strrchr(filename, '.');
158 strcpy(ext, "_pal.tga");
159 R_WritePalTGA( filename, normals, globalImages->compressedPalette, width, height);
164 if ( glConfig.sharedTexturePaletteAvailable ) {
165 qglTexImage2D( GL_TEXTURE_2D,
178 //=======================================================================
181 static byte mipBlendColors[16][4] = {
204 This may need to scan six cube map images
207 GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, int width, int height,
208 textureDepth_t minimumDepth, bool *monochromeResult ) const {
211 int rgbOr, rgbAnd, aOr, aAnd;
212 int rgbDiffer, rgbaDiffer;
214 // determine if the rgb channels are all the same
215 // and if either all rgb or all alpha are 255
224 *monochromeResult = true; // until shown otherwise
226 for ( int side = 0 ; side < numDataPtrs ; side++ ) {
227 scan = dataPtrs[side];
228 for ( i = 0; i < c; i++, scan += 4 ) {
234 cor = scan[0] | scan[1] | scan[2];
235 cand = scan[0] & scan[1] & scan[2];
237 // if rgb are all the same, the or and and will match
238 rgbDiffer |= ( cor ^ cand );
240 // our "isMonochrome" test is more lax than rgbDiffer,
241 // allowing the values to be off by several units and
242 // still use the NV20 mono path
243 if ( *monochromeResult ) {
244 if ( abs( scan[0] - scan[1] ) > 16
245 || abs( scan[0] - scan[2] ) > 16 ) {
246 *monochromeResult = false;
256 rgbaDiffer |= ( cor ^ cand );
260 // we assume that all 0 implies that the alpha channel isn't needed,
261 // because some tools will spit out 32 bit images with a 0 alpha instead
262 // of 255 alpha, but if the alpha actually is referenced, there will be
263 // different behavior in the compressed vs uncompressed states.
265 if ( aAnd == 255 || aOr == 0 ) {
271 // catch normal maps first
272 if ( minimumDepth == TD_BUMP ) {
273 if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() == 1 && glConfig.sharedTexturePaletteAvailable ) {
274 // image_useNormalCompression should only be set to 1 on nv_10 and nv_20 paths
275 return GL_COLOR_INDEX8_EXT;
276 } else if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() && glConfig.textureCompressionAvailable ) {
277 // image_useNormalCompression == 2 uses rxgb format which produces really good quality for medium settings
278 return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
280 // we always need the alpha channel for bump maps for swizzling
285 // allow a complete override of image compression with a cvar
286 if ( !globalImages->image_useCompression.GetBool() ) {
287 minimumDepth = TD_HIGH_QUALITY;
290 if ( minimumDepth == TD_SPECULAR ) {
291 // we are assuming that any alpha channel is unintentional
292 if ( glConfig.textureCompressionAvailable ) {
293 return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
298 if ( minimumDepth == TD_DIFFUSE ) {
299 // we might intentionally have an alpha channel for alpha tested textures
300 if ( glConfig.textureCompressionAvailable ) {
302 return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
304 return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
306 } else if ( ( aAnd == 255 || aOr == 0 ) ) {
313 // there will probably be some drivers that don't
314 // correctly handle the intensity/alpha/luminance/luminance+alpha
315 // formats, so provide a fallback that only uses the rgb/rgba formats
316 if ( !globalImages->image_useAllFormats.GetBool() ) {
317 // pretend rgb is varying and inconsistant, which
318 // prevents any of the more compact forms
324 // cases without alpha
326 if ( minimumDepth == TD_HIGH_QUALITY ) {
327 return GL_RGB8; // four bytes
329 if ( glConfig.textureCompressionAvailable ) {
330 return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // half byte
332 return GL_RGB5; // two bytes
337 if ( minimumDepth != TD_HIGH_QUALITY && glConfig.textureCompressionAvailable ) {
338 return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
340 return GL_INTENSITY8; // single byte for all channels
344 // we don't support alpha textures any more, because there
345 // is a discrepancy in the definition of TEX_ENV_COMBINE that
346 // causes them to be treated as 0 0 0 A, instead of 1 1 1 A as
347 // normal texture modulation treats them
348 if ( rgbAnd == 255 ) {
349 return GL_ALPHA8; // single byte, only alpha
353 if ( minimumDepth == TD_HIGH_QUALITY ) {
354 return GL_RGBA8; // four bytes
356 if ( glConfig.textureCompressionAvailable ) {
357 return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
360 return GL_LUMINANCE8_ALPHA8; // two bytes, max quality
362 return GL_RGBA4; // two bytes
367 SetImageFilterAndRepeat
370 void idImage::SetImageFilterAndRepeat() const {
371 // set the minimize / maximize filtering
374 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
375 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
378 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
379 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
382 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
383 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
386 common->FatalError( "R_CreateImage: bad texture filter" );
389 if ( glConfig.anisotropicAvailable ) {
390 // only do aniso filtering on mip mapped images
391 if ( filter == TF_DEFAULT ) {
392 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, globalImages->textureAnisotropy );
394 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
397 if ( glConfig.textureLODBiasAvailable ) {
398 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS_EXT, globalImages->textureLODBias );
401 // set the wrap/clamp modes
404 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
405 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
407 case TR_CLAMP_TO_BORDER:
408 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
409 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
411 case TR_CLAMP_TO_ZERO:
412 case TR_CLAMP_TO_ZERO_ALPHA:
414 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
415 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
418 common->FatalError( "R_CreateImage: bad texture repeat" );
425 helper function that takes the current width/height and might make them smaller
428 void idImage::GetDownsize( int &scaled_width, int &scaled_height ) const {
431 // perform optional picmip operation to save texture memory
432 if ( depth == TD_SPECULAR && globalImages->image_downSizeSpecular.GetInteger() ) {
433 size = globalImages->image_downSizeSpecularLimit.GetInteger();
437 } else if ( depth == TD_BUMP && globalImages->image_downSizeBump.GetInteger() ) {
438 size = globalImages->image_downSizeBumpLimit.GetInteger();
442 } else if ( ( allowDownSize || globalImages->image_forceDownSize.GetBool() ) && globalImages->image_downSize.GetInteger() ) {
443 size = globalImages->image_downSizeLimit.GetInteger();
450 while ( scaled_width > size || scaled_height > size ) {
451 if ( scaled_width > 1 ) {
454 if ( scaled_height > 1 ) {
460 // clamp to minimum size
461 if ( scaled_width < 1 ) {
464 if ( scaled_height < 1 ) {
468 // clamp size to the hardware specific upper limit
469 // scale both axis down equally so we don't have to
470 // deal with a half mip resampling
471 // This causes a 512*256 texture to sample down to
472 // 256*128 on a voodoo3, even though it could be 256*256
473 while ( scaled_width > glConfig.maxTextureSize
474 || scaled_height > glConfig.maxTextureSize ) {
484 The alpha channel bytes should be 255 if you don't
487 We need a material characteristic to ask for specific texture modes.
489 Designed limitations of flexibility:
491 No support for texture borders.
493 No support for texture border color.
495 No support for texture environment colors or GL_BLEND or GL_DECAL
496 texture environments, because the automatic optimization to single
497 or dual component textures makes those modes potentially undefined.
499 No non-power-of-two images.
501 No palettized textures.
503 There is no way to specify separate wrap/clamp values for S and T
505 There is no way to specify explicit mip map levels
509 void idImage::GenerateImage( const byte *pic, int width, int height,
510 textureFilter_t filterParm, bool allowDownSizeParm,
511 textureRepeat_t repeatParm, textureDepth_t depthParm ) {
514 int scaled_width, scaled_height;
520 allowDownSize = allowDownSizeParm;
524 // if we don't have a rendering context, just return after we
525 // have filled in the parms. We must have the values set, or
526 // an image match from a shader before OpenGL starts would miss
527 // the generated texture
528 if ( !glConfig.isInitialized ) {
532 // don't let mip mapping smear the texture into the clamped border
533 if ( repeat == TR_CLAMP_TO_ZERO ) {
534 preserveBorder = true;
536 preserveBorder = false;
539 // make sure it is a power of 2
540 scaled_width = MakePowerOfTwo( width );
541 scaled_height = MakePowerOfTwo( height );
543 if ( scaled_width != width || scaled_height != height ) {
544 common->Error( "R_CreateImage: not a power of 2 image" );
547 // Optionally modify our width/height based on options/hardware
548 GetDownsize( scaled_width, scaled_height );
552 // generate the texture number
553 qglGenTextures( 1, &texnum );
555 // select proper internal format before we resample
556 internalFormat = SelectInternalFormat( &pic, 1, width, height, depth, &isMonochrome );
558 // copy or resample data as appropriate for first MIP level
559 if ( ( scaled_width == width ) && ( scaled_height == height ) ) {
560 // we must copy even if unchanged, because the border zeroing
561 // would otherwise modify const data
562 scaledBuffer = (byte *)R_StaticAlloc( sizeof( unsigned ) * scaled_width * scaled_height );
563 memcpy (scaledBuffer, pic, width*height*4);
565 // resample down as needed (FIXME: this doesn't seem like it resamples anymore!)
566 // scaledBuffer = R_ResampleTexture( pic, width, height, width >>= 1, height >>= 1 );
567 scaledBuffer = R_MipMap( pic, width, height, preserveBorder );
577 while ( width > scaled_width || height > scaled_height ) {
578 shrunk = R_MipMap( scaledBuffer, width, height, preserveBorder );
579 R_StaticFree( scaledBuffer );
580 scaledBuffer = shrunk;
592 // one might have shrunk down below the target size
593 scaled_width = width;
594 scaled_height = height;
597 uploadHeight = scaled_height;
598 uploadWidth = scaled_width;
601 // zero the border if desired, allowing clamped projection textures
602 // even after picmip resampling or careless artists.
603 if ( repeat == TR_CLAMP_TO_ZERO ) {
606 rgba[0] = rgba[1] = rgba[2] = 0;
608 R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
610 if ( repeat == TR_CLAMP_TO_ZERO_ALPHA ) {
613 rgba[0] = rgba[1] = rgba[2] = 255;
615 R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
618 if ( generatorFunction == NULL && ( depth == TD_BUMP && globalImages->image_writeNormalTGA.GetBool() || depth != TD_BUMP && globalImages->image_writeTGA.GetBool() ) ) {
619 // Optionally write out the texture to a .tga
620 char filename[MAX_IMAGE_NAME];
621 ImageProgramStringToCompressedFileName( imgName, filename );
622 char *ext = strrchr(filename, '.');
624 strcpy( ext, ".tga" );
625 // swap the red/alpha for the write
627 if ( depth == TD_BUMP ) {
628 for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
629 scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
630 scaledBuffer[ i + 3 ] = 0;
634 R_WriteTGA( filename, scaledBuffer, scaled_width, scaled_height, false );
638 if ( depth == TD_BUMP ) {
639 for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
640 scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
641 scaledBuffer[ i ] = 0;
648 // swap the red and alpha for rxgb support
649 // do this even on tga normal maps so we only have to use
650 // one fragment program
651 // if the image is precompressed ( either in palletized mode or true rxgb mode )
652 // then it is loaded above and the swap never happens here
653 if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 1 ) {
654 for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
655 scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
656 scaledBuffer[ i ] = 0;
659 // upload the main image level
663 if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
665 if ( depth == TD_BUMP ) {
666 for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
667 scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
668 scaledBuffer[ i + 3 ] = 0;
672 UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, 0 );
674 qglTexImage2D( GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
677 // create and upload the mip map levels, which we do in all cases, even if we don't think they are needed
681 while ( scaled_width > 1 || scaled_height > 1 ) {
682 // preserve the border after mip map unless repeating
683 shrunk = R_MipMap( scaledBuffer, scaled_width, scaled_height, preserveBorder );
684 R_StaticFree( scaledBuffer );
685 scaledBuffer = shrunk;
689 if ( scaled_width < 1 ) {
692 if ( scaled_height < 1 ) {
697 // this is a visualization tool that shades each mip map
698 // level with a different color so you can see the
699 // rasterizer's texture level selection algorithm
700 // Changing the color doesn't help with lumminance/alpha/intensity formats...
701 if ( depth == TD_DIFFUSE && globalImages->image_colorMipLevels.GetBool() ) {
702 R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] );
705 // upload the mip map
706 if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
707 UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, miplevel );
709 qglTexImage2D( GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height,
710 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
714 if ( scaledBuffer != 0 ) {
715 R_StaticFree( scaledBuffer );
718 SetImageFilterAndRepeat();
720 // see if we messed anything up
730 void idImage::Generate3DImage( const byte *pic, int width, int height, int picDepth,
731 textureFilter_t filterParm, bool allowDownSizeParm,
732 textureRepeat_t repeatParm, textureDepth_t minDepthParm ) {
733 int scaled_width, scaled_height, scaled_depth;
738 allowDownSize = allowDownSizeParm;
740 depth = minDepthParm;
742 // if we don't have a rendering context, just return after we
743 // have filled in the parms. We must have the values set, or
744 // an image match from a shader before OpenGL starts would miss
745 // the generated texture
746 if ( !glConfig.isInitialized ) {
750 // make sure it is a power of 2
751 scaled_width = MakePowerOfTwo( width );
752 scaled_height = MakePowerOfTwo( height );
753 scaled_depth = MakePowerOfTwo( picDepth );
754 if ( scaled_width != width || scaled_height != height || scaled_depth != picDepth ) {
755 common->Error( "R_Create3DImage: not a power of 2 image" );
758 // FIXME: allow picmip here
760 // generate the texture number
761 qglGenTextures( 1, &texnum );
763 // select proper internal format before we resample
764 // this function doesn't need to know it is 3D, so just make it very "tall"
765 internalFormat = SelectInternalFormat( &pic, 1, width, height * picDepth, minDepthParm, &isMonochrome );
767 uploadHeight = scaled_height;
768 uploadWidth = scaled_width;
769 uploadDepth = scaled_depth;
774 // upload the main image level
777 qglTexImage3D(GL_TEXTURE_3D, 0, internalFormat, scaled_width, scaled_height, scaled_depth,
778 0, GL_RGBA, GL_UNSIGNED_BYTE, pic );
780 // create and upload the mip map levels
782 byte *scaledBuffer, *shrunk;
784 scaledBuffer = (byte *)R_StaticAlloc( scaled_width * scaled_height * scaled_depth * 4 );
785 memcpy( scaledBuffer, pic, scaled_width * scaled_height * scaled_depth * 4 );
787 while ( scaled_width > 1 || scaled_height > 1 || scaled_depth > 1 ) {
788 // preserve the border after mip map unless repeating
789 shrunk = R_MipMap3D( scaledBuffer, scaled_width, scaled_height, scaled_depth,
790 (bool)(repeat != TR_REPEAT) );
791 R_StaticFree( scaledBuffer );
792 scaledBuffer = shrunk;
797 if ( scaled_width < 1 ) {
800 if ( scaled_height < 1 ) {
803 if ( scaled_depth < 1 ) {
808 // upload the mip map
809 qglTexImage3D(GL_TEXTURE_3D, miplevel, internalFormat, scaled_width, scaled_height, scaled_depth,
810 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
812 R_StaticFree( scaledBuffer );
814 // set the minimize / maximize filtering
817 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
818 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
821 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
822 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
825 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
826 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
829 common->FatalError( "R_CreateImage: bad texture filter" );
832 // set the wrap/clamp modes
835 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT );
836 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT );
837 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT );
839 case TR_CLAMP_TO_BORDER:
840 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
841 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
843 case TR_CLAMP_TO_ZERO:
844 case TR_CLAMP_TO_ZERO_ALPHA:
846 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
847 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
848 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
851 common->FatalError( "R_CreateImage: bad texture repeat" );
854 // see if we messed anything up
863 Non-square cube sides are not allowed
866 void idImage::GenerateCubeImage( const byte *pic[6], int size,
867 textureFilter_t filterParm, bool allowDownSizeParm,
868 textureDepth_t depthParm ) {
869 int scaled_width, scaled_height;
876 allowDownSize = allowDownSizeParm;
881 // if we don't have a rendering context, just return after we
882 // have filled in the parms. We must have the values set, or
883 // an image match from a shader before OpenGL starts would miss
884 // the generated texture
885 if ( !glConfig.isInitialized ) {
889 if ( ! glConfig.cubeMapAvailable ) {
893 width = height = size;
895 // generate the texture number
896 qglGenTextures( 1, &texnum );
898 // select proper internal format before we resample
899 internalFormat = SelectInternalFormat( pic, 6, width, height, depth, &isMonochrome );
901 // don't bother with downsample for now
902 scaled_width = width;
903 scaled_height = height;
905 uploadHeight = scaled_height;
906 uploadWidth = scaled_width;
910 // no other clamp mode makes sense
911 qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
912 qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
914 // set the minimize / maximize filtering
917 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
918 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
921 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
922 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
925 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
926 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
929 common->FatalError( "R_CreateImage: bad texture filter" );
932 // upload the base level
933 // FIXME: support GL_COLOR_INDEX8_EXT?
934 for ( i = 0 ; i < 6 ; i++ ) {
935 qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, internalFormat, scaled_width, scaled_height, 0,
936 GL_RGBA, GL_UNSIGNED_BYTE, pic[i] );
940 // create and upload the mip map levels
944 for ( i = 0 ; i < 6 ; i++ ) {
945 shrunk[i] = R_MipMap( pic[i], scaled_width, scaled_height, false );
949 while ( scaled_width > 1 ) {
950 for ( i = 0 ; i < 6 ; i++ ) {
953 qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, miplevel, internalFormat,
954 scaled_width / 2, scaled_height / 2, 0,
955 GL_RGBA, GL_UNSIGNED_BYTE, shrunk[i] );
957 if ( scaled_width > 2 ) {
958 shrunken = R_MipMap( shrunk[i], scaled_width/2, scaled_height/2, false );
963 R_StaticFree( shrunk[i] );
964 shrunk[i] = shrunken;
972 // see if we messed anything up
979 ImageProgramStringToFileCompressedFileName
982 void idImage::ImageProgramStringToCompressedFileName( const char *imageProg, char *fileName ) const {
986 strcpy( fileName, "dds/" );
987 f = fileName + strlen( fileName );
991 // convert all illegal characters to underscores
992 // this could conceivably produce a duplicated mapping, but we aren't going to worry about it
993 for ( s = imageProg ; *s ; s++ ) {
994 if ( *s == '/' || *s == '\\' || *s == '(') {
1002 } else if ( *s == '<' || *s == '>' || *s == ':' || *s == '|' || *s == '"' || *s == '.' ) {
1005 } else if ( *s == ' ' && *(f-1) == '/' ) { // ignore a space right after a slash
1006 } else if ( *s == ')' || *s == ',' ) { // always ignore these
1013 strcat( fileName, ".dds" );
1018 NumLevelsForImageSize
1021 int idImage::NumLevelsForImageSize( int width, int height ) const {
1024 while ( width > 1 || height > 1 ) {
1035 WritePrecompressedImage
1037 When we are happy with our source data, we can write out precompressed
1038 versions of everything to speed future load times.
1041 void idImage::WritePrecompressedImage() {
1043 // Always write the precompressed image if we're making a build
1044 if ( !com_makingBuild.GetBool() ) {
1045 if ( !globalImages->image_writePrecompressedTextures.GetBool() || !globalImages->image_usePrecompressedTextures.GetBool() ) {
1050 if ( !glConfig.isInitialized ) {
1054 char filename[MAX_IMAGE_NAME];
1055 ImageProgramStringToCompressedFileName( imgName, filename );
1059 int numLevels = NumLevelsForImageSize( uploadWidth, uploadHeight );
1060 if ( numLevels > MAX_TEXTURE_LEVELS ) {
1061 common->Warning( "R_WritePrecompressedImage: level > MAX_TEXTURE_LEVELS for image %s", filename );
1065 // glGetTexImage only supports a small subset of all the available internal formats
1066 // We have to use BGRA because DDS is a windows based format
1067 int altInternalFormat = 0;
1069 switch ( internalFormat ) {
1070 case GL_COLOR_INDEX8_EXT:
1071 case GL_COLOR_INDEX:
1072 // this will not work with dds viewers but we need it in this format to save disk
1073 // load speed ( i.e. size )
1074 altInternalFormat = GL_COLOR_INDEX;
1082 altInternalFormat = GL_BGR_EXT;
1085 case GL_LUMINANCE8_ALPHA8:
1088 altInternalFormat = GL_BGRA_EXT;
1092 altInternalFormat = GL_ALPHA;
1096 if ( FormatIsDXT( internalFormat ) ) {
1097 altInternalFormat = internalFormat;
1099 common->Warning("Unknown or unsupported format for %s", filename);
1104 if ( globalImages->image_useOffLineCompression.GetBool() && FormatIsDXT( altInternalFormat ) ) {
1105 idStr outFile = fileSystem->RelativePathToOSPath( filename, "fs_basepath" );
1106 idStr inFile = outFile;
1107 inFile.StripFileExtension();
1108 inFile.SetFileExtension( "tga" );
1110 if ( depth == TD_BUMP ) {
1111 format = "RXGB +red 0.0 +green 0.5 +blue 0.5";
1113 switch ( altInternalFormat ) {
1114 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1117 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1118 format = "DXT1 -alpha_threshold";
1120 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1123 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1128 globalImages->AddDDSCommand( va( "z:/d3xp/compressonator/thecompressonator -convert \"%s\" \"%s\" %s -mipmaps\n", inFile.c_str(), outFile.c_str(), format.c_str() ) );
1133 ddsFileHeader_t header;
1134 memset( &header, 0, sizeof(header) );
1135 header.dwSize = sizeof(header);
1136 header.dwFlags = DDSF_CAPS | DDSF_PIXELFORMAT | DDSF_WIDTH | DDSF_HEIGHT;
1137 header.dwHeight = uploadHeight;
1138 header.dwWidth = uploadWidth;
1140 // hack in our monochrome flag for the NV20 optimization
1141 if ( isMonochrome ) {
1142 header.dwFlags |= DDSF_ID_MONOCHROME;
1145 if ( FormatIsDXT( altInternalFormat ) ) {
1146 // size (in bytes) of the compressed base image
1147 header.dwFlags |= DDSF_LINEARSIZE;
1148 header.dwPitchOrLinearSize = ( ( uploadWidth + 3 ) / 4 ) * ( ( uploadHeight + 3 ) / 4 )*
1149 (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1152 // 4 Byte aligned line width (from nv_dds)
1153 header.dwFlags |= DDSF_PITCH;
1154 header.dwPitchOrLinearSize = ( ( uploadWidth * bitSize + 31 ) & -32 ) >> 3;
1157 header.dwCaps1 = DDSF_TEXTURE;
1159 if ( numLevels > 1 ) {
1160 header.dwMipMapCount = numLevels;
1161 header.dwFlags |= DDSF_MIPMAPCOUNT;
1162 header.dwCaps1 |= DDSF_MIPMAP | DDSF_COMPLEX;
1165 header.ddspf.dwSize = sizeof(header.ddspf);
1166 if ( FormatIsDXT( altInternalFormat ) ) {
1167 header.ddspf.dwFlags = DDSF_FOURCC;
1168 switch ( altInternalFormat ) {
1169 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1170 header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
1172 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1173 header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
1174 header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
1176 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1177 header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','3');
1179 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1180 header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','5');
1184 header.ddspf.dwFlags = ( internalFormat == GL_COLOR_INDEX8_EXT ) ? DDSF_RGB | DDSF_ID_INDEXCOLOR : DDSF_RGB;
1185 header.ddspf.dwRGBBitCount = bitSize;
1186 switch ( altInternalFormat ) {
1188 case GL_LUMINANCE_ALPHA:
1189 header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
1190 header.ddspf.dwABitMask = 0xFF000000;
1194 case GL_COLOR_INDEX:
1195 header.ddspf.dwRBitMask = 0x00FF0000;
1196 header.ddspf.dwGBitMask = 0x0000FF00;
1197 header.ddspf.dwBBitMask = 0x000000FF;
1200 header.ddspf.dwFlags = DDSF_ALPHAPIXELS;
1201 header.ddspf.dwABitMask = 0xFF000000;
1204 common->Warning( "Unknown or unsupported format for %s", filename );
1209 idFile *f = fileSystem->OpenFileWrite( filename );
1211 common->Warning( "Could not open %s trying to write precompressed image", filename );
1214 common->Printf( "Writing precompressed image: %s\n", filename );
1216 f->Write( "DDS ", 4 );
1217 f->Write( &header, sizeof(header) );
1219 // bind to the image so we can read back the contents
1222 qglPixelStorei( GL_PACK_ALIGNMENT, 1 ); // otherwise small rows get padded to 32 bits
1224 int uw = uploadWidth;
1225 int uh = uploadHeight;
1227 // Will be allocated first time through the loop
1230 for ( int level = 0 ; level < numLevels ; level++ ) {
1233 if ( FormatIsDXT( altInternalFormat ) ) {
1234 size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
1235 (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1237 size = uw * uh * (bitSize / 8);
1241 data = (byte *)R_StaticAlloc( size );
1244 if ( FormatIsDXT( altInternalFormat ) ) {
1245 qglGetCompressedTexImageARB( GL_TEXTURE_2D, level, data );
1247 qglGetTexImage( GL_TEXTURE_2D, level, altInternalFormat, GL_UNSIGNED_BYTE, data );
1250 f->Write( data, size );
1263 R_StaticFree( data );
1266 fileSystem->CloseFile( f );
1271 ShouldImageBePartialCached
1273 Returns true if there is a precompressed image, and it is large enough
1277 bool idImage::ShouldImageBePartialCached() {
1278 if ( !glConfig.textureCompressionAvailable ) {
1282 if ( !globalImages->image_useCache.GetBool() ) {
1286 // the allowDownSize flag does double-duty as don't-partial-load
1287 if ( !allowDownSize ) {
1291 if ( globalImages->image_cacheMinK.GetInteger() <= 0 ) {
1295 // if we are doing a copyFiles, make sure the original images are referenced
1296 if ( fileSystem->PerformingCopyFiles() ) {
1300 char filename[MAX_IMAGE_NAME];
1301 ImageProgramStringToCompressedFileName( imgName, filename );
1303 // get the file timestamp
1304 fileSystem->ReadFile( filename, NULL, ×tamp );
1306 if ( timestamp == FILE_NOT_FOUND_TIMESTAMP ) {
1310 // open it and get the file size
1313 f = fileSystem->OpenFileRead( filename );
1318 int len = f->Length();
1319 fileSystem->CloseFile( f );
1321 if ( len <= globalImages->image_cacheMinK.GetInteger() * 1024 ) {
1325 // we do want to do a partial load
1331 CheckPrecompressedImage
1333 If fullLoad is false, only the small mip levels of the image will be loaded
1336 bool idImage::CheckPrecompressedImage( bool fullLoad ) {
1337 if ( !glConfig.isInitialized || !glConfig.textureCompressionAvailable ) {
1341 #if 1 // ( _D3XP had disabled ) - Allow grabbing of DDS's from original Doom pak files
1342 // if we are doing a copyFiles, make sure the original images are referenced
1343 if ( fileSystem->PerformingCopyFiles() ) {
1348 if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 2 ) {
1352 // god i love last minute hacks :-)
1353 if ( com_machineSpec.GetInteger() >= 1 && com_videoRam.GetInteger() >= 128 && imgName.Icmpn( "lights/", 7 ) == 0 ) {
1357 char filename[MAX_IMAGE_NAME];
1358 ImageProgramStringToCompressedFileName( imgName, filename );
1360 // get the file timestamp
1361 ID_TIME_T precompTimestamp;
1362 fileSystem->ReadFile( filename, NULL, &precompTimestamp );
1365 if ( precompTimestamp == FILE_NOT_FOUND_TIMESTAMP ) {
1369 if ( !generatorFunction && timestamp != FILE_NOT_FOUND_TIMESTAMP ) {
1370 if ( precompTimestamp < timestamp ) {
1371 // The image has changed after being precompressed
1376 timestamp = precompTimestamp;
1378 // open it and just read the header
1381 f = fileSystem->OpenFileRead( filename );
1386 int len = f->Length();
1387 if ( len < sizeof( ddsFileHeader_t ) ) {
1388 fileSystem->CloseFile( f );
1392 if ( !fullLoad && len > globalImages->image_cacheMinK.GetInteger() * 1024 ) {
1393 len = globalImages->image_cacheMinK.GetInteger() * 1024;
1396 byte *data = (byte *)R_StaticAlloc( len );
1398 f->Read( data, len );
1400 fileSystem->CloseFile( f );
1402 unsigned long magic = LittleLong( *(unsigned long *)data );
1403 ddsFileHeader_t *_header = (ddsFileHeader_t *)(data + 4);
1404 int ddspf_dwFlags = LittleLong( _header->ddspf.dwFlags );
1406 if ( magic != DDS_MAKEFOURCC('D', 'D', 'S', ' ')) {
1407 common->Printf( "CheckPrecompressedImage( %s ): magic != 'DDS '\n", imgName.c_str() );
1408 R_StaticFree( data );
1412 // if we don't support color index textures, we must load the full image
1413 // should we just expand the 256 color image to 32 bit for upload?
1414 if ( ddspf_dwFlags & DDSF_ID_INDEXCOLOR && !glConfig.sharedTexturePaletteAvailable ) {
1415 R_StaticFree( data );
1419 // upload all the levels
1420 UploadPrecompressedImage( data, len );
1422 R_StaticFree( data );
1429 UploadPrecompressedImage
1431 This can be called by the front end during nromal loading,
1432 or by the backend after a background read of the file
1436 void idImage::UploadPrecompressedImage( byte *data, int len ) {
1437 ddsFileHeader_t *header = (ddsFileHeader_t *)(data + 4);
1439 // ( not byte swapping dwReserved1 dwReserved2 )
1440 header->dwSize = LittleLong( header->dwSize );
1441 header->dwFlags = LittleLong( header->dwFlags );
1442 header->dwHeight = LittleLong( header->dwHeight );
1443 header->dwWidth = LittleLong( header->dwWidth );
1444 header->dwPitchOrLinearSize = LittleLong( header->dwPitchOrLinearSize );
1445 header->dwDepth = LittleLong( header->dwDepth );
1446 header->dwMipMapCount = LittleLong( header->dwMipMapCount );
1447 header->dwCaps1 = LittleLong( header->dwCaps1 );
1448 header->dwCaps2 = LittleLong( header->dwCaps2 );
1450 header->ddspf.dwSize = LittleLong( header->ddspf.dwSize );
1451 header->ddspf.dwFlags = LittleLong( header->ddspf.dwFlags );
1452 header->ddspf.dwFourCC = LittleLong( header->ddspf.dwFourCC );
1453 header->ddspf.dwRGBBitCount = LittleLong( header->ddspf.dwRGBBitCount );
1454 header->ddspf.dwRBitMask = LittleLong( header->ddspf.dwRBitMask );
1455 header->ddspf.dwGBitMask = LittleLong( header->ddspf.dwGBitMask );
1456 header->ddspf.dwBBitMask = LittleLong( header->ddspf.dwBBitMask );
1457 header->ddspf.dwABitMask = LittleLong( header->ddspf.dwABitMask );
1459 // generate the texture number
1460 qglGenTextures( 1, &texnum );
1462 int externalFormat = 0;
1464 precompressedFile = true;
1466 uploadWidth = header->dwWidth;
1467 uploadHeight = header->dwHeight;
1468 if ( header->ddspf.dwFlags & DDSF_FOURCC ) {
1469 switch ( header->ddspf.dwFourCC ) {
1470 case DDS_MAKEFOURCC( 'D', 'X', 'T', '1' ):
1471 if ( header->ddspf.dwFlags & DDSF_ALPHAPIXELS ) {
1472 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1474 internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1477 case DDS_MAKEFOURCC( 'D', 'X', 'T', '3' ):
1478 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
1480 case DDS_MAKEFOURCC( 'D', 'X', 'T', '5' ):
1481 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1483 case DDS_MAKEFOURCC( 'R', 'X', 'G', 'B' ):
1484 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1487 common->Warning( "Invalid compressed internal format\n" );
1490 } else if ( ( header->ddspf.dwFlags & DDSF_RGBA ) && header->ddspf.dwRGBBitCount == 32 ) {
1491 externalFormat = GL_BGRA_EXT;
1492 internalFormat = GL_RGBA8;
1493 } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 32 ) {
1494 externalFormat = GL_BGRA_EXT;
1495 internalFormat = GL_RGBA8;
1496 } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 24 ) {
1497 if ( header->ddspf.dwFlags & DDSF_ID_INDEXCOLOR ) {
1498 externalFormat = GL_COLOR_INDEX;
1499 internalFormat = GL_COLOR_INDEX8_EXT;
1501 externalFormat = GL_BGR_EXT;
1502 internalFormat = GL_RGB8;
1504 } else if ( header->ddspf.dwRGBBitCount == 8 ) {
1505 externalFormat = GL_ALPHA;
1506 internalFormat = GL_ALPHA8;
1508 common->Warning( "Invalid uncompressed internal format\n" );
1512 // we need the monochrome flag for the NV20 optimized path
1513 if ( header->dwFlags & DDSF_ID_MONOCHROME ) {
1514 isMonochrome = true;
1517 type = TT_2D; // FIXME: we may want to support pre-compressed cube maps in the future
1522 if ( header->dwFlags & DDSF_MIPMAPCOUNT ) {
1523 numMipmaps = header->dwMipMapCount;
1526 int uw = uploadWidth;
1527 int uh = uploadHeight;
1529 // We may skip some mip maps if we are downsizing
1531 GetDownsize( uploadWidth, uploadHeight );
1533 byte *imagedata = data + sizeof(ddsFileHeader_t) + 4;
1535 for ( int i = 0 ; i < numMipmaps; i++ ) {
1537 if ( FormatIsDXT( internalFormat ) ) {
1538 size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
1539 (internalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1541 size = uw * uh * (header->ddspf.dwRGBBitCount / 8);
1544 if ( uw > uploadWidth || uh > uploadHeight ) {
1547 if ( FormatIsDXT( internalFormat ) ) {
1548 qglCompressedTexImage2DARB( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, size, imagedata );
1550 qglTexImage2D( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, externalFormat, GL_UNSIGNED_BYTE, imagedata );
1565 SetImageFilterAndRepeat();
1572 Absolutely every image goes through this path
1573 On exit, the idImage will have a valid OpenGL texture number that can be bound
1576 void idImage::ActuallyLoadImage( bool checkForPrecompressed, bool fromBackEnd ) {
1580 // this is the ONLY place generatorFunction will ever be called
1581 if ( generatorFunction ) {
1582 generatorFunction( this );
1586 // if we are a partial image, we are only going to load from a compressed file
1587 if ( isPartialImage ) {
1588 if ( CheckPrecompressedImage( false ) ) {
1591 // this is an error -- the partial image failed to load
1597 // load the image from disk
1599 if ( cubeFiles != CF_2D ) {
1602 // we don't check for pre-compressed cube images currently
1603 R_LoadCubeImages( imgName, cubeFiles, pics, &width, ×tamp );
1605 if ( pics[0] == NULL ) {
1606 common->Warning( "Couldn't load cube image: %s", imgName.c_str() );
1611 GenerateCubeImage( (const byte **)pics, width, filter, allowDownSize, depth );
1612 precompressedFile = false;
1614 for ( int i = 0 ; i < 6 ; i++ ) {
1616 R_StaticFree( pics[i] );
1620 // see if we have a pre-generated image file that is
1621 // already image processed and compressed
1622 if ( checkForPrecompressed && globalImages->image_usePrecompressedTextures.GetBool() ) {
1623 if ( CheckPrecompressedImage( true ) ) {
1624 // we got the precompressed image
1627 // fall through to load the normal image
1630 R_LoadImageProgram( imgName, &pic, &width, &height, ×tamp, &depth );
1632 if ( pic == NULL ) {
1633 common->Warning( "Couldn't load image: %s", imgName.c_str() );
1638 // swap the red and alpha for rxgb support
1639 // do this even on tga normal maps so we only have to use
1640 // one fragment program
1641 // if the image is precompressed ( either in palletized mode or true rxgb mode )
1642 // then it is loaded above and the swap never happens here
1643 if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 1 ) {
1644 for ( int i = 0; i < width * height * 4; i += 4 ) {
1645 pic[ i + 3 ] = pic[ i ];
1650 // build a hash for checking duplicate image files
1651 // NOTE: takes about 10% of image load times (SD)
1652 // may not be strictly necessary, but some code uses it, so let's leave it in
1653 imageHash = MD4_BlockChecksum( pic, width * height * 4 );
1655 GenerateImage( pic, width, height, filter, allowDownSize, repeat, depth );
1656 timestamp = timestamp;
1657 precompressedFile = false;
1659 R_StaticFree( pic );
1661 // write out the precompressed version of this file if needed
1662 WritePrecompressedImage();
1666 //=========================================================================================================
1673 void idImage::PurgeImage() {
1674 if ( texnum != TEXTURE_NOT_LOADED ) {
1675 // sometimes is NULL when exiting with an error
1676 if ( qglDeleteTextures ) {
1677 qglDeleteTextures( 1, &texnum ); // this should be the ONLY place it is ever called!
1679 texnum = TEXTURE_NOT_LOADED;
1682 // clear all the current binding caches, so the next bind will do a real one
1683 for ( int i = 0 ; i < MAX_MULTITEXTURE_UNITS ; i++ ) {
1684 backEnd.glState.tmu[i].current2DMap = -1;
1685 backEnd.glState.tmu[i].current3DMap = -1;
1686 backEnd.glState.tmu[i].currentCubeMap = -1;
1694 Automatically enables 2D mapping, cube mapping, or 3D texturing if needed
1697 void idImage::Bind() {
1699 RB_LogComment( "idImage::Bind( %s )\n", imgName.c_str() );
1702 // if this is an image that we are caching, move it to the front of the LRU chain
1703 if ( partialImage ) {
1704 if ( cacheUsageNext ) {
1705 // unlink from old position
1706 cacheUsageNext->cacheUsagePrev = cacheUsagePrev;
1707 cacheUsagePrev->cacheUsageNext = cacheUsageNext;
1709 // link in at the head of the list
1710 cacheUsageNext = globalImages->cacheLRU.cacheUsageNext;
1711 cacheUsagePrev = &globalImages->cacheLRU;
1713 cacheUsageNext->cacheUsagePrev = this;
1714 cacheUsagePrev->cacheUsageNext = this;
1717 // load the image if necessary (FIXME: not SMP safe!)
1718 if ( texnum == TEXTURE_NOT_LOADED ) {
1719 if ( partialImage ) {
1720 // if we have a partial image, go ahead and use that
1721 this->partialImage->Bind();
1723 // start a background load of the full thing if it isn't already in the queue
1724 if ( !backgroundLoadInProgress ) {
1725 StartBackgroundImageLoad();
1730 // load the image on demand here, which isn't our normal game operating mode
1731 ActuallyLoadImage( true, true ); // check for precompressed, load is from back end
1735 // bump our statistic counters
1736 frameUsed = backEnd.frameCount;
1739 tmu_t *tmu = &backEnd.glState.tmu[backEnd.glState.currenttmu];
1741 // enable or disable apropriate texture modes
1742 if ( tmu->textureType != type && ( backEnd.glState.currenttmu < glConfig.maxTextureUnits ) ) {
1743 if ( tmu->textureType == TT_CUBIC ) {
1744 qglDisable( GL_TEXTURE_CUBE_MAP_EXT );
1745 } else if ( tmu->textureType == TT_3D ) {
1746 qglDisable( GL_TEXTURE_3D );
1747 } else if ( tmu->textureType == TT_2D ) {
1748 qglDisable( GL_TEXTURE_2D );
1751 if ( type == TT_CUBIC ) {
1752 qglEnable( GL_TEXTURE_CUBE_MAP_EXT );
1753 } else if ( type == TT_3D ) {
1754 qglEnable( GL_TEXTURE_3D );
1755 } else if ( type == TT_2D ) {
1756 qglEnable( GL_TEXTURE_2D );
1758 tmu->textureType = type;
1762 if ( type == TT_2D ) {
1763 if ( tmu->current2DMap != texnum ) {
1764 tmu->current2DMap = texnum;
1765 qglBindTexture( GL_TEXTURE_2D, texnum );
1767 } else if ( type == TT_CUBIC ) {
1768 if ( tmu->currentCubeMap != texnum ) {
1769 tmu->currentCubeMap = texnum;
1770 qglBindTexture( GL_TEXTURE_CUBE_MAP_EXT, texnum );
1772 } else if ( type == TT_3D ) {
1773 if ( tmu->current3DMap != texnum ) {
1774 tmu->current3DMap = texnum;
1775 qglBindTexture( GL_TEXTURE_3D, texnum );
1779 if ( com_purgeAll.GetBool() ) {
1780 GLclampf priority = 1.0f;
1781 qglPrioritizeTextures( 1, &texnum, &priority );
1789 Fragment programs explicitly say which type of map they want, so we don't need to
1790 do any enable / disable changes
1793 void idImage::BindFragment() {
1795 RB_LogComment( "idImage::BindFragment %s )\n", imgName.c_str() );
1798 // if this is an image that we are caching, move it to the front of the LRU chain
1799 if ( partialImage ) {
1800 if ( cacheUsageNext ) {
1801 // unlink from old position
1802 cacheUsageNext->cacheUsagePrev = cacheUsagePrev;
1803 cacheUsagePrev->cacheUsageNext = cacheUsageNext;
1805 // link in at the head of the list
1806 cacheUsageNext = globalImages->cacheLRU.cacheUsageNext;
1807 cacheUsagePrev = &globalImages->cacheLRU;
1809 cacheUsageNext->cacheUsagePrev = this;
1810 cacheUsagePrev->cacheUsageNext = this;
1813 // load the image if necessary (FIXME: not SMP safe!)
1814 if ( texnum == TEXTURE_NOT_LOADED ) {
1815 if ( partialImage ) {
1816 // if we have a partial image, go ahead and use that
1817 this->partialImage->BindFragment();
1819 // start a background load of the full thing if it isn't already in the queue
1820 if ( !backgroundLoadInProgress ) {
1821 StartBackgroundImageLoad();
1826 // load the image on demand here, which isn't our normal game operating mode
1827 ActuallyLoadImage( true, true ); // check for precompressed, load is from back end
1831 // bump our statistic counters
1832 frameUsed = backEnd.frameCount;
1836 if ( type == TT_2D ) {
1837 qglBindTexture( GL_TEXTURE_2D, texnum );
1838 } else if ( type == TT_RECT ) {
1839 qglBindTexture( GL_TEXTURE_RECTANGLE_NV, texnum );
1840 } else if ( type == TT_CUBIC ) {
1841 qglBindTexture( GL_TEXTURE_CUBE_MAP_EXT, texnum );
1842 } else if ( type == TT_3D ) {
1843 qglBindTexture( GL_TEXTURE_3D, texnum );
1849 ====================
1851 ====================
1853 void idImage::CopyFramebuffer( int x, int y, int imageWidth, int imageHeight, bool useOversizedBuffer ) {
1856 if ( cvarSystem->GetCVarBool( "g_lowresFullscreenFX" ) ) {
1861 // if the size isn't a power of 2, the image must be increased in size
1862 int potWidth, potHeight;
1864 potWidth = MakePowerOfTwo( imageWidth );
1865 potHeight = MakePowerOfTwo( imageHeight );
1867 GetDownsize( imageWidth, imageHeight );
1868 GetDownsize( potWidth, potHeight );
1870 qglReadBuffer( GL_BACK );
1872 // only resize if the current dimensions can't hold it at all,
1873 // otherwise subview renderings could thrash this
1874 if ( ( useOversizedBuffer && ( uploadWidth < potWidth || uploadHeight < potHeight ) )
1875 || ( !useOversizedBuffer && ( uploadWidth != potWidth || uploadHeight != potHeight ) ) ) {
1876 uploadWidth = potWidth;
1877 uploadHeight = potHeight;
1878 if ( potWidth == imageWidth && potHeight == imageHeight ) {
1879 qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, x, y, imageWidth, imageHeight, 0 );
1882 // we need to create a dummy image with power of two dimensions,
1883 // then do a qglCopyTexSubImage2D of the data we want
1884 // this might be a 16+ meg allocation, which could fail on _alloca
1885 junk = (byte *)Mem_Alloc( potWidth * potHeight * 4 );
1886 memset( junk, 0, potWidth * potHeight * 4 ); //!@#
1887 #if 0 // Disabling because it's unnecessary and introduces a green strip on edge of _currentRender
1888 for ( int i = 0 ; i < potWidth * potHeight * 4 ; i+=4 ) {
1892 qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, potWidth, potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, junk );
1895 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1898 // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1899 // it and don't try and do a texture compression or some other silliness
1900 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1903 // if the image isn't a full power of two, duplicate an extra row and/or column to fix bilerps
1904 if ( imageWidth != potWidth ) {
1905 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, imageWidth, 0, x+imageWidth-1, y, 1, imageHeight );
1907 if ( imageHeight != potHeight ) {
1908 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, imageHeight, x, y+imageHeight-1, imageWidth, 1 );
1911 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1912 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1914 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1915 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
1917 backEnd.c_copyFrameBuffer++;
1921 ====================
1924 This should just be part of copyFramebuffer once we have a proper image type field
1925 ====================
1927 void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight ) {
1930 // if the size isn't a power of 2, the image must be increased in size
1931 int potWidth, potHeight;
1933 potWidth = MakePowerOfTwo( imageWidth );
1934 potHeight = MakePowerOfTwo( imageHeight );
1936 if ( uploadWidth != potWidth || uploadHeight != potHeight ) {
1937 uploadWidth = potWidth;
1938 uploadHeight = potHeight;
1939 if ( potWidth == imageWidth && potHeight == imageHeight ) {
1940 qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, 0 );
1942 // we need to create a dummy image with power of two dimensions,
1943 // then do a qglCopyTexSubImage2D of the data we want
1944 qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
1945 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1948 // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1949 // it and don't try and do a texture compression or some other silliness
1950 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1953 // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
1954 // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
1956 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1957 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
1962 RB_UploadScratchImage
1964 if rows = cols * 6, assume it is a cube map animation
1967 void idImage::UploadScratch( const byte *data, int cols, int rows ) {
1970 // if rows = cols * 6, assume it is a cube map animation
1971 if ( rows == cols * 6 ) {
1972 if ( type != TT_CUBIC ) {
1974 uploadWidth = -1; // for a non-sub upload
1980 // if the scratchImage isn't in the format we want, specify it as a new texture
1981 if ( cols != uploadWidth || rows != uploadHeight ) {
1983 uploadHeight = rows;
1985 // upload the base level
1986 for ( i = 0 ; i < 6 ; i++ ) {
1987 qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, GL_RGB8, cols, rows, 0,
1988 GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
1991 // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1992 // it and don't try and do a texture compression
1993 for ( i = 0 ; i < 6 ; i++ ) {
1994 qglTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, 0, 0, cols, rows,
1995 GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
1998 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1999 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
2000 // no other clamp mode makes sense
2001 qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2002 qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2004 // otherwise, it is a 2D image
2005 if ( type != TT_2D ) {
2007 uploadWidth = -1; // for a non-sub upload
2012 // if the scratchImage isn't in the format we want, specify it as a new texture
2013 if ( cols != uploadWidth || rows != uploadHeight ) {
2015 uploadHeight = rows;
2016 qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
2018 // otherwise, just subimage upload it so that drivers can tell we are going to be changing
2019 // it and don't try and do a texture compression
2020 qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
2022 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
2023 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
2024 // these probably should be clamp, but we have a lot of issues with editor
2025 // geometry coming out with texcoords slightly off one side, resulting in
2026 // a smear across the entire polygon
2028 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
2029 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
2031 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
2032 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
2038 void idImage::SetClassification( int tag ) {
2039 classification = tag;
2047 int idImage::StorageSize() const {
2050 if ( texnum == TEXTURE_NOT_LOADED ) {
2057 baseSize = uploadWidth*uploadHeight;
2060 baseSize = uploadWidth*uploadHeight*uploadDepth;
2063 baseSize = 6 * uploadWidth*uploadHeight;
2067 baseSize *= BitsForInternalFormat( internalFormat );
2071 // account for mip mapping
2072 baseSize = baseSize * 4 / 3;
2082 void idImage::Print() const {
2083 if ( precompressedFile ) {
2084 common->Printf( "P" );
2085 } else if ( generatorFunction ) {
2086 common->Printf( "F" );
2088 common->Printf( " " );
2093 common->Printf( " " );
2096 common->Printf( "3" );
2099 common->Printf( "C" );
2102 common->Printf( "R" );
2105 common->Printf( "<BAD TYPE:%i>", type );
2109 common->Printf( "%4i %4i ", uploadWidth, uploadHeight );
2113 common->Printf( "dflt " );
2116 common->Printf( "linr " );
2119 common->Printf( "nrst " );
2122 common->Printf( "<BAD FILTER:%i>", filter );
2126 switch ( internalFormat ) {
2129 common->Printf( "I " );
2132 case GL_LUMINANCE8_ALPHA8:
2133 common->Printf( "LA " );
2136 common->Printf( "RGB " );
2139 common->Printf( "RGBA " );
2142 common->Printf( "L " );
2145 common->Printf( "A " );
2148 common->Printf( "RGBA8 " );
2151 common->Printf( "RGB8 " );
2153 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
2154 common->Printf( "DXT1 " );
2156 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
2157 common->Printf( "DXT1A " );
2159 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
2160 common->Printf( "DXT3 " );
2162 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
2163 common->Printf( "DXT5 " );
2166 common->Printf( "RGBA4 " );
2169 common->Printf( "RGB5 " );
2171 case GL_COLOR_INDEX8_EXT:
2172 common->Printf( "CI8 " );
2174 case GL_COLOR_INDEX:
2175 common->Printf( "CI " );
2177 case GL_COMPRESSED_RGB_ARB:
2178 common->Printf( "RGBC " );
2180 case GL_COMPRESSED_RGBA_ARB:
2181 common->Printf( "RGBAC " );
2184 common->Printf( " " );
2187 common->Printf( "<BAD FORMAT:%i>", internalFormat );
2193 common->Printf( "rept " );
2195 case TR_CLAMP_TO_ZERO:
2196 common->Printf( "zero " );
2198 case TR_CLAMP_TO_ZERO_ALPHA:
2199 common->Printf( "azro " );
2202 common->Printf( "clmp " );
2205 common->Printf( "<BAD REPEAT:%i>", repeat );
2209 common->Printf( "%4ik ", StorageSize() / 1024 );
2211 common->Printf( " %s\n", imgName.c_str() );