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"
34 const char *imageFilter[] = {
35 "GL_LINEAR_MIPMAP_NEAREST",
36 "GL_LINEAR_MIPMAP_LINEAR",
39 "GL_NEAREST_MIPMAP_NEAREST",
40 "GL_NEAREST_MIPMAP_LINEAR",
44 idCVar idImageManager::image_filter( "image_filter", imageFilter[1], CVAR_RENDERER | CVAR_ARCHIVE, "changes texture filtering on mipmapped images", imageFilter, idCmdSystem::ArgCompletion_String<imageFilter> );
45 idCVar idImageManager::image_anisotropy( "image_anisotropy", "1", CVAR_RENDERER | CVAR_ARCHIVE, "set the maximum texture anisotropy if available" );
46 idCVar idImageManager::image_lodbias( "image_lodbias", "0", CVAR_RENDERER | CVAR_ARCHIVE, "change lod bias on mipmapped images" );
47 idCVar idImageManager::image_downSize( "image_downSize", "0", CVAR_RENDERER | CVAR_ARCHIVE, "controls texture downsampling" );
48 idCVar idImageManager::image_forceDownSize( "image_forceDownSize", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "" );
49 idCVar idImageManager::image_roundDown( "image_roundDown", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "round bad sizes down to nearest power of two" );
50 idCVar idImageManager::image_colorMipLevels( "image_colorMipLevels", "0", CVAR_RENDERER | CVAR_BOOL, "development aid to see texture mip usage" );
51 idCVar idImageManager::image_preload( "image_preload", "1", CVAR_RENDERER | CVAR_BOOL | CVAR_ARCHIVE, "if 0, dynamically load all images" );
52 idCVar idImageManager::image_useCompression( "image_useCompression", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "0 = force everything to high quality" );
53 idCVar idImageManager::image_useAllFormats( "image_useAllFormats", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "allow alpha/intensity/luminance/luminance+alpha" );
54 idCVar idImageManager::image_useNormalCompression( "image_useNormalCompression", "2", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "2 = use rxgb compression for normal maps, 1 = use 256 color compression for normal maps if available" );
55 idCVar idImageManager::image_usePrecompressedTextures( "image_usePrecompressedTextures", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "use .dds files if present" );
56 idCVar idImageManager::image_writePrecompressedTextures( "image_writePrecompressedTextures", "0", CVAR_RENDERER | CVAR_BOOL, "write .dds files if necessary" );
57 idCVar idImageManager::image_writeNormalTGA( "image_writeNormalTGA", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the final normal maps for debugging" );
58 idCVar idImageManager::image_writeNormalTGAPalletized( "image_writeNormalTGAPalletized", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the final palletized normal maps for debugging" );
59 idCVar idImageManager::image_writeTGA( "image_writeTGA", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the non normal maps for debugging" );
60 idCVar idImageManager::image_useOffLineCompression( "image_useOfflineCompression", "0", CVAR_RENDERER | CVAR_BOOL, "write a batch file for offline compression of DDS files" );
61 idCVar idImageManager::image_cacheMinK( "image_cacheMinK", "200", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "maximum KB of precompressed files to read at specification time" );
62 idCVar idImageManager::image_cacheMegs( "image_cacheMegs", "20", CVAR_RENDERER | CVAR_ARCHIVE, "maximum MB set aside for temporary loading of full-sized precompressed images" );
63 idCVar idImageManager::image_useCache( "image_useCache", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "1 = do background load image caching" );
64 idCVar idImageManager::image_showBackgroundLoads( "image_showBackgroundLoads", "0", CVAR_RENDERER | CVAR_BOOL, "1 = print number of outstanding background loads" );
65 idCVar idImageManager::image_downSizeSpecular( "image_downSizeSpecular", "0", CVAR_RENDERER | CVAR_ARCHIVE, "controls specular downsampling" );
66 idCVar idImageManager::image_downSizeBump( "image_downSizeBump", "0", CVAR_RENDERER | CVAR_ARCHIVE, "controls normal map downsampling" );
67 idCVar idImageManager::image_downSizeSpecularLimit( "image_downSizeSpecularLimit", "64", CVAR_RENDERER | CVAR_ARCHIVE, "controls specular downsampled limit" );
68 idCVar idImageManager::image_downSizeBumpLimit( "image_downSizeBumpLimit", "128", CVAR_RENDERER | CVAR_ARCHIVE, "controls normal map downsample limit" );
69 idCVar idImageManager::image_ignoreHighQuality( "image_ignoreHighQuality", "0", CVAR_RENDERER | CVAR_ARCHIVE, "ignore high quality setting on materials" );
70 idCVar idImageManager::image_downSizeLimit( "image_downSizeLimit", "256", CVAR_RENDERER | CVAR_ARCHIVE, "controls diffuse map downsample limit" );
71 // do this with a pointer, in case we want to make the actual manager
72 // a private virtual subclass
73 idImageManager imageManager;
74 idImageManager *globalImages = &imageManager;
77 enum IMAGE_CLASSIFICATION {
90 struct imageClassificate_t {
98 typedef idList< int > intList;
100 const imageClassificate_t IC_Info[] = {
101 { "models/characters", "Characters", IC_NPC, 512, 512 },
102 { "models/weapons", "Weapons", IC_WEAPON, 512, 512 },
103 { "models/monsters", "Monsters", IC_MONSTER, 512, 512 },
104 { "models/mapobjects", "Model Geometry", IC_MODELGEOMETRY, 512, 512 },
105 { "models/items", "Items", IC_ITEMS, 512, 512 },
106 { "models", "Other model textures", IC_MODELSOTHER, 512, 512 },
107 { "guis/assets", "Guis", IC_GUIS, 256, 256 },
108 { "textures", "World Geometry", IC_WORLDGEOMETRY, 256, 256 },
109 { "", "Other", IC_OTHER, 256, 256 }
114 static int ClassifyImage( const char *name ) {
117 for ( int i = 0; i < IC_COUNT; i++ ) {
118 if ( str.Find( IC_Info[i].rootPath, false ) == 0 ) {
119 return IC_Info[i].type;
129 Creates a 0-255 ramp image
132 static void R_RampImage( idImage *image ) {
136 for (x=0 ; x<256 ; x++) {
143 image->GenerateImage( (byte *)data, 256, 1,
144 TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
151 Creates a ramp that matches our fudged specular calculation
154 static void R_SpecularTableImage( idImage *image ) {
158 for (x=0 ; x<256 ; x++) {
163 // this is the behavior of the hacked up fragment programs that
164 // can't really do a power function
171 int b = (int)(f * 255);
179 image->GenerateImage( (byte *)data, 256, 1,
180 TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
186 R_Specular2DTableImage
188 Create a 2D table that calculates ( reflection dot , specularity )
191 static void R_Specular2DTableImage( idImage *image ) {
193 byte data[256][256][4];
195 memset( data, 0, sizeof( data ) );
196 for ( x = 0 ; x < 256 ; x++ ) {
197 float f = x / 255.0f;
198 for ( y = 0; y < 256; y++ ) {
200 int b = (int)( pow( f, y ) * 255.0f );
202 // as soon as b equals zero all remaining values in this column are going to be zero
203 // we early out to avoid pow() underflows
214 image->GenerateImage( (byte *)data, 256, 256, TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
223 Creates a 0-255 ramp image
226 static void R_AlphaRampImage( idImage *image ) {
230 for (x=0 ; x<256 ; x++) {
237 image->GenerateImage( (byte *)data, 256, 1,
238 TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
247 the default image will be grey with a white box outline
248 to allow you to see the mapping coordinates on a surface
251 #define DEFAULT_SIZE 16
252 void idImage::MakeDefault() {
254 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
256 if ( com_developer.GetBool() ) {
258 for ( y = 0 ; y < DEFAULT_SIZE ; y++ ) {
259 for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
268 for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
279 data[DEFAULT_SIZE-1][x][0] =
280 data[DEFAULT_SIZE-1][x][1] =
281 data[DEFAULT_SIZE-1][x][2] =
282 data[DEFAULT_SIZE-1][x][3] = 255;
284 data[x][DEFAULT_SIZE-1][0] =
285 data[x][DEFAULT_SIZE-1][1] =
286 data[x][DEFAULT_SIZE-1][2] =
287 data[x][DEFAULT_SIZE-1][3] = 255;
290 for ( y = 0 ; y < DEFAULT_SIZE ; y++ ) {
291 for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
300 GenerateImage( (byte *)data,
301 DEFAULT_SIZE, DEFAULT_SIZE,
302 TF_DEFAULT, true, TR_REPEAT, TD_DEFAULT );
307 static void R_DefaultImage( idImage *image ) {
308 image->MakeDefault();
311 static void R_WhiteImage( idImage *image ) {
312 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
314 // solid white texture
315 memset( data, 255, sizeof( data ) );
316 image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
317 TF_DEFAULT, false, TR_REPEAT, TD_DEFAULT );
320 static void R_BlackImage( idImage *image ) {
321 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
323 // solid black texture
324 memset( data, 0, sizeof( data ) );
325 image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
326 TF_DEFAULT, false, TR_REPEAT, TD_DEFAULT );
330 // the size determines how far away from the edge the blocks start fading
331 static const int BORDER_CLAMP_SIZE = 32;
332 static void R_BorderClampImage( idImage *image ) {
333 byte data[BORDER_CLAMP_SIZE][BORDER_CLAMP_SIZE][4];
335 // solid white texture with a single pixel black border
336 memset( data, 255, sizeof( data ) );
337 for ( int i = 0 ; i < BORDER_CLAMP_SIZE ; i++ ) {
343 data[i][BORDER_CLAMP_SIZE-1][0] =
344 data[i][BORDER_CLAMP_SIZE-1][1] =
345 data[i][BORDER_CLAMP_SIZE-1][2] =
346 data[i][BORDER_CLAMP_SIZE-1][3] =
353 data[BORDER_CLAMP_SIZE-1][i][0] =
354 data[BORDER_CLAMP_SIZE-1][i][1] =
355 data[BORDER_CLAMP_SIZE-1][i][2] =
356 data[BORDER_CLAMP_SIZE-1][i][3] = 0;
359 image->GenerateImage( (byte *)data, BORDER_CLAMP_SIZE, BORDER_CLAMP_SIZE,
360 TF_LINEAR /* TF_NEAREST */, false, TR_CLAMP_TO_BORDER, TD_DEFAULT );
362 if ( !glConfig.isInitialized ) {
363 // can't call qglTexParameterfv yet
366 // explicit zero border
368 color[0] = color[1] = color[2] = color[3] = 0;
369 qglTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color );
372 static void R_RGBA8Image( idImage *image ) {
373 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
375 memset( data, 0, sizeof( data ) );
381 image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
382 TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
385 static void R_RGB8Image( idImage *image ) {
386 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
388 memset( data, 0, sizeof( data ) );
394 image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
395 TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
398 static void R_AlphaNotchImage( idImage *image ) {
401 // this is used for alpha test clip planes
403 data[0][0] = data[0][1] = data[0][2] = 255;
405 data[1][0] = data[1][1] = data[1][2] = 255;
408 image->GenerateImage( (byte *)data, 2, 1,
409 TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
412 static void R_FlatNormalImage( idImage *image ) {
413 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
416 int red = ( globalImages->image_useNormalCompression.GetInteger() == 1 ) ? 0 : 3;
417 int alpha = ( red == 0 ) ? 3 : 0;
418 // flat normal map for default bunp mapping
419 for ( i = 0 ; i < 4 ; i++ ) {
420 data[0][i][red] = 128;
423 data[0][i][alpha] = 255;
425 image->GenerateImage( (byte *)data, 2, 2,
426 TF_DEFAULT, true, TR_REPEAT, TD_HIGH_QUALITY );
429 static void R_AmbientNormalImage( idImage *image ) {
430 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
433 int red = ( globalImages->image_useNormalCompression.GetInteger() == 1 ) ? 0 : 3;
434 int alpha = ( red == 0 ) ? 3 : 0;
435 // flat normal map for default bunp mapping
436 for ( i = 0 ; i < 4 ; i++ ) {
437 data[0][i][red] = (byte)(255 * tr.ambientLightVector[0]);
438 data[0][i][1] = (byte)(255 * tr.ambientLightVector[1]);
439 data[0][i][2] = (byte)(255 * tr.ambientLightVector[2]);
440 data[0][i][alpha] = 255;
443 for ( i = 0 ; i < 6 ; i++ ) {
444 pics[i] = data[0][0];
446 // this must be a cube map for fragment programs to simply substitute for the normalization cube map
447 image->GenerateCubeImage( pics, 2, TF_DEFAULT, true, TD_HIGH_QUALITY );
451 static void CreateSquareLight( void ) {
458 width = height = 128;
460 buffer = (byte *)R_StaticAlloc( 128 * 128 * 4 );
462 for ( x = 0 ; x < 128 ; x++ ) {
465 } else if ( x > 96 ) {
470 for ( y = 0 ; y < 128 ; y++ ) {
473 } else if ( y > 96 ) {
478 d = (byte)idMath::Sqrt( dx * dx + dy * dy );
486 buffer[(y*128+x)*4+0] =
487 buffer[(y*128+x)*4+1] =
488 buffer[(y*128+x)*4+2] = d;
489 buffer[(y*128+x)*4+3] = 255;
493 R_WriteTGA( "lights/squarelight.tga", buffer, width, height );
495 R_StaticFree( buffer );
498 static void CreateFlashOff( void ) {
507 buffer = (byte *)R_StaticAlloc( width * height * 4 );
509 for ( x = 0 ; x < width ; x++ ) {
510 for ( y = 0 ; y < height ; y++ ) {
511 d = 255 - ( x * 256 / width );
512 buffer[(y*width+x)*4+0] =
513 buffer[(y*width+x)*4+1] =
514 buffer[(y*width+x)*4+2] = d;
515 buffer[(y*width+x)*4+3] = 255;
519 R_WriteTGA( "lights/flashoff.tga", buffer, width, height );
521 R_StaticFree( buffer );
530 void CreatePitFogImage( void ) {
531 byte data[16][16][4];
534 memset( data, 0, sizeof( data ) );
535 for ( i = 0 ; i < 16 ; i++ ) {
550 for ( j = 0 ; j < 16 ; j++ ) {
558 R_WriteTGA( "shapes/pitFalloff.tga", data[0][0], 16, 16 );
563 CreatealphaSquareImage
566 void CreatealphaSquareImage( void ) {
567 byte data[16][16][4];
570 for ( i = 0 ; i < 16 ; i++ ) {
573 for ( j = 0 ; j < 16 ; j++ ) {
574 if ( i == 0 || i == 15 || j == 0 || j == 15 ) {
586 R_WriteTGA( "shapes/alphaSquare.tga", data[0][0], 16, 16 );
589 #define NORMAL_MAP_SIZE 32
591 /*** NORMALIZATION CUBE MAP CONSTRUCTION ***/
593 /* Given a cube map face index, cube map size, and integer 2D face position,
594 * return the cooresponding normalized vector.
596 static void getCubeVector(int i, int cubesize, int x, int y, float *vector) {
597 float s, t, sc, tc, mag;
599 s = ((float)x + 0.5) / (float)cubesize;
600 t = ((float)y + 0.5) / (float)cubesize;
637 mag = idMath::InvSqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]);
643 /* Initialize a cube map texture object that generates RGB values
644 * that when expanded to a [-1,1] range in the register combiners
645 * form a normalized vector matching the per-pixel vector used to
646 * access the cube map.
648 static void makeNormalizeVectorCubeMap( idImage *image ) {
654 size = NORMAL_MAP_SIZE;
656 pixels[0] = (GLubyte*) Mem_Alloc(size*size*4*6);
658 for (i = 0; i < 6; i++) {
659 pixels[i] = pixels[0] + i*size*size*4;
660 for (y = 0; y < size; y++) {
661 for (x = 0; x < size; x++) {
662 getCubeVector(i, size, x, y, vector);
663 pixels[i][4*(y*size+x) + 0] = (byte)(128 + 127*vector[0]);
664 pixels[i][4*(y*size+x) + 1] = (byte)(128 + 127*vector[1]);
665 pixels[i][4*(y*size+x) + 2] = (byte)(128 + 127*vector[2]);
666 pixels[i][4*(y*size+x) + 3] = 255;
671 image->GenerateCubeImage( (const byte **)pixels, size,
672 TF_LINEAR, false, TD_HIGH_QUALITY );
682 R_CreateNoFalloffImage
684 This is a solid white texture that is zero clamped.
687 static void R_CreateNoFalloffImage( idImage *image ) {
689 byte data[16][FALLOFF_TEXTURE_SIZE][4];
691 memset( data, 0, sizeof( data ) );
692 for (x=1 ; x<FALLOFF_TEXTURE_SIZE-1 ; x++) {
693 for (y=1 ; y<15 ; y++) {
700 image->GenerateImage( (byte *)data, FALLOFF_TEXTURE_SIZE, 16,
701 TF_DEFAULT, false, TR_CLAMP_TO_ZERO, TD_HIGH_QUALITY );
709 We calculate distance correctly in two planes, but the
710 third will still be projection based
713 const int FOG_SIZE = 128;
715 void R_FogImage( idImage *image ) {
717 byte data[FOG_SIZE][FOG_SIZE][4];
722 float remaining = 1.0;
723 for ( i = 0 ; i < 256 ; i++ ) {
728 for (x=0 ; x<FOG_SIZE ; x++) {
729 for (y=0 ; y<FOG_SIZE ; y++) {
732 d = idMath::Sqrt( (x - FOG_SIZE/2) * (x - FOG_SIZE/2)
733 + (y - FOG_SIZE/2) * (y - FOG_SIZE / 2) );
739 } else if ( b > 255 ) {
742 b = (byte)(255 * ( 1.0 - step[b] ));
743 if ( x == 0 || x == FOG_SIZE-1 || y == 0 || y == FOG_SIZE-1 ) {
744 b = 255; // avoid clamping issues
753 image->GenerateImage( (byte *)data, FOG_SIZE, FOG_SIZE,
754 TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
762 Height values below zero are inside the fog volume
765 static const float RAMP_RANGE = 8;
766 static const float DEEP_RANGE = -30;
767 static float FogFraction( float viewHeight, float targetHeight ) {
768 float total = idMath::Fabs( targetHeight - viewHeight );
770 // return targetHeight >= 0 ? 0 : 1.0;
772 // only ranges that cross the ramp range are special
773 if ( targetHeight > 0 && viewHeight > 0 ) {
776 if ( targetHeight < -RAMP_RANGE && viewHeight < -RAMP_RANGE ) {
781 if ( targetHeight > 0 ) {
782 above = targetHeight;
783 } else if ( viewHeight > 0 ) {
789 float rampTop, rampBottom;
791 if ( viewHeight > targetHeight ) {
792 rampTop = viewHeight;
793 rampBottom = targetHeight;
795 rampTop = targetHeight;
796 rampBottom = viewHeight;
801 if ( rampBottom < -RAMP_RANGE ) {
802 rampBottom = -RAMP_RANGE;
805 float rampSlope = 1.0 / RAMP_RANGE;
808 return -viewHeight * rampSlope;
811 float ramp = ( 1.0 - ( rampTop * rampSlope + rampBottom * rampSlope ) * -0.5 ) * ( rampTop - rampBottom );
813 float frac = ( total - above - ramp ) / total;
815 // after it gets moderately deep, always use full value
816 float deepest = viewHeight < targetHeight ? viewHeight : targetHeight;
818 float deepFrac = deepest / DEEP_RANGE;
819 if ( deepFrac >= 1.0 ) {
823 frac = frac * ( 1.0 - deepFrac ) + deepFrac;
832 Modulate the fog alpha density based on the distance of the
833 start and end points to the terminator plane
836 void R_FogEnterImage( idImage *image ) {
838 byte data[FOG_ENTER_SIZE][FOG_ENTER_SIZE][4];
841 for (x=0 ; x<FOG_ENTER_SIZE ; x++) {
842 for (y=0 ; y<FOG_ENTER_SIZE ; y++) {
845 d = FogFraction( x - (FOG_ENTER_SIZE / 2), y - (FOG_ENTER_SIZE / 2) );
850 } else if ( b > 255 ) {
860 // if mipmapped, acutely viewed surfaces fade wrong
861 image->GenerateImage( (byte *)data, FOG_ENTER_SIZE, FOG_ENTER_SIZE,
862 TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
872 static const int QUADRATIC_WIDTH = 32;
873 static const int QUADRATIC_HEIGHT = 4;
875 void R_QuadraticImage( idImage *image ) {
877 byte data[QUADRATIC_HEIGHT][QUADRATIC_WIDTH][4];
881 for (x=0 ; x<QUADRATIC_WIDTH ; x++) {
882 for (y=0 ; y<QUADRATIC_HEIGHT ; y++) {
885 d = x - (QUADRATIC_WIDTH/2 - 0.5);
886 d = idMath::Fabs( d );
888 d /= QUADRATIC_WIDTH/2;
896 } else if ( b > 255 ) {
906 image->GenerateImage( (byte *)data, QUADRATIC_WIDTH, QUADRATIC_HEIGHT,
907 TF_DEFAULT, false, TR_CLAMP, TD_HIGH_QUALITY );
910 //=====================================================================
915 int minimize, maximize;
924 This resets filtering on all loaded images
925 New images will automatically pick up the current values.
928 void idImageManager::ChangeTextureFilter( void ) {
932 static filterName_t textureFilters[] = {
933 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
934 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR},
935 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
936 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
937 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
938 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}
941 // if these are changed dynamically, it will force another ChangeTextureFilter
942 image_filter.ClearModified();
943 image_anisotropy.ClearModified();
944 image_lodbias.ClearModified();
946 string = image_filter.GetString();
947 for ( i = 0; i < 6; i++ ) {
948 if ( !idStr::Icmp( textureFilters[i].name, string ) ) {
954 common->Warning( "bad r_textureFilter: '%s'", string);
955 // default to LINEAR_MIPMAP_NEAREST
959 // set the values for future images
960 textureMinFilter = textureFilters[i].minimize;
961 textureMaxFilter = textureFilters[i].maximize;
962 textureAnisotropy = image_anisotropy.GetFloat();
963 if ( textureAnisotropy < 1 ) {
964 textureAnisotropy = 1;
965 } else if ( textureAnisotropy > glConfig.maxTextureAnisotropy ) {
966 textureAnisotropy = glConfig.maxTextureAnisotropy;
968 textureLODBias = image_lodbias.GetFloat();
970 // change all the existing mipmap texture objects with default filtering
972 for ( i = 0 ; i < images.Num() ; i++ ) {
973 unsigned int texEnum = GL_TEXTURE_2D;
977 switch( glt->type ) {
979 texEnum = GL_TEXTURE_2D;
982 texEnum = GL_TEXTURE_3D;
985 texEnum = GL_TEXTURE_CUBE_MAP_EXT;
989 // make sure we don't start a background load
990 if ( glt->texnum == idImage::TEXTURE_NOT_LOADED ) {
994 if ( glt->filter == TF_DEFAULT ) {
995 qglTexParameterf(texEnum, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
996 qglTexParameterf(texEnum, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
998 if ( glConfig.anisotropicAvailable ) {
999 qglTexParameterf(texEnum, GL_TEXTURE_MAX_ANISOTROPY_EXT, globalImages->textureAnisotropy );
1001 if ( glConfig.textureLODBiasAvailable ) {
1002 qglTexParameterf(texEnum, GL_TEXTURE_LOD_BIAS_EXT, globalImages->textureLODBias );
1012 void idImage::Reload( bool checkPrecompressed, bool force ) {
1013 // always regenerate functional images
1014 if ( generatorFunction ) {
1015 common->DPrintf( "regenerating %s.\n", imgName.c_str() );
1016 generatorFunction( this );
1024 if ( cubeFiles != CF_2D ) {
1025 R_LoadCubeImages( imgName, cubeFiles, NULL, NULL, ¤t );
1027 // get the current values
1028 R_LoadImageProgram( imgName, NULL, NULL, NULL, ¤t );
1030 if ( current <= timestamp ) {
1035 common->DPrintf( "reloading %s.\n", imgName.c_str() );
1039 // force no precompressed image check, which will cause it to be reloaded
1040 // from source, and another precompressed file generated.
1041 // Load is from the front end, so the back end must be synced
1042 ActuallyLoadImage( checkPrecompressed, false );
1049 Regenerate all images that came directly from files that have changed, so
1050 any saved changes will show up in place.
1052 New r_texturesize/r_texturedepth variables will take effect on reload
1057 void R_ReloadImages_f( const idCmdArgs &args ) {
1061 bool checkPrecompressed;
1063 // this probably isn't necessary...
1064 globalImages->ChangeTextureFilter();
1067 checkPrecompressed = false; // if we are doing this as a vid_restart, look for precompressed like normal
1069 if ( args.Argc() == 2 ) {
1070 if ( !idStr::Icmp( args.Argv(1), "all" ) ) {
1072 } else if ( !idStr::Icmp( args.Argv(1), "reload" ) ) {
1074 checkPrecompressed = true;
1076 common->Printf( "USAGE: reloadImages <all>\n" );
1081 for ( i = 0 ; i < globalImages->images.Num() ; i++ ) {
1082 image = globalImages->images[ i ];
1083 image->Reload( checkPrecompressed, all );
1093 =======================
1096 =======================
1098 static int R_QsortImageSizes( const void *a, const void *b ) {
1099 const sortedImage_t *ea, *eb;
1101 ea = (sortedImage_t *)a;
1102 eb = (sortedImage_t *)b;
1104 if ( ea->size > eb->size ) {
1107 if ( ea->size < eb->size ) {
1110 return idStr::Icmp( ea->image->imgName, eb->image->imgName );
1118 void R_ListImages_f( const idCmdArgs &args ) {
1119 int i, j, partialSize;
1124 bool uncompressedOnly = false;
1125 bool unloaded = false;
1126 bool partial = false;
1127 bool cached = false;
1128 bool uncached = false;
1129 bool failed = false;
1130 bool touched = false;
1131 bool sorted = false;
1132 bool duplicated = false;
1133 bool byClassification = false;
1134 bool overSized = false;
1136 if ( args.Argc() == 1 ) {
1138 } else if ( args.Argc() == 2 ) {
1139 if ( idStr::Icmp( args.Argv( 1 ), "uncompressed" ) == 0 ) {
1140 uncompressedOnly = true;
1141 } else if ( idStr::Icmp( args.Argv( 1 ), "sorted" ) == 0 ) {
1143 } else if ( idStr::Icmp( args.Argv( 1 ), "partial" ) == 0 ) {
1145 } else if ( idStr::Icmp( args.Argv( 1 ), "unloaded" ) == 0 ) {
1147 } else if ( idStr::Icmp( args.Argv( 1 ), "cached" ) == 0 ) {
1149 } else if ( idStr::Icmp( args.Argv( 1 ), "uncached" ) == 0 ) {
1151 } else if ( idStr::Icmp( args.Argv( 1 ), "tagged" ) == 0 ) {
1153 } else if ( idStr::Icmp( args.Argv( 1 ), "duplicated" ) == 0 ) {
1155 } else if ( idStr::Icmp( args.Argv( 1 ), "touched" ) == 0 ) {
1157 } else if ( idStr::Icmp( args.Argv( 1 ), "classify" ) == 0 ) {
1158 byClassification = true;
1160 } else if ( idStr::Icmp( args.Argv( 1 ), "oversized" ) == 0 ) {
1161 byClassification = true;
1172 common->Printf( "usage: listImages [ sorted | partial | unloaded | cached | uncached | tagged | duplicated | touched | classify | showOverSized ]\n" );
1176 const char *header = " -w-- -h-- filt -fmt-- wrap size --name-------\n";
1177 common->Printf( "\n%s", header );
1181 sortedImage_t *sortedArray = (sortedImage_t *)alloca( sizeof( sortedImage_t ) * globalImages->images.Num() );
1183 for ( i = 0 ; i < globalImages->images.Num() ; i++ ) {
1184 image = globalImages->images[ i ];
1186 if ( uncompressedOnly ) {
1187 if ( ( image->internalFormat >= GL_COMPRESSED_RGB_S3TC_DXT1_EXT && image->internalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT )
1188 || image->internalFormat == GL_COLOR_INDEX8_EXT ) {
1193 if ( matchTag && image->classification != matchTag ) {
1196 if ( unloaded && image->texnum != idImage::TEXTURE_NOT_LOADED ) {
1199 if ( partial && !image->isPartialImage ) {
1202 if ( cached && ( !image->partialImage || image->texnum == idImage::TEXTURE_NOT_LOADED ) ) {
1205 if ( uncached && ( !image->partialImage || image->texnum != idImage::TEXTURE_NOT_LOADED ) ) {
1209 // only print duplicates (from mismatched wrap / clamp, etc)
1212 for ( j = i+1 ; j < globalImages->images.Num() ; j++ ) {
1213 if ( idStr::Icmp( image->imgName, globalImages->images[ j ]->imgName ) == 0 ) {
1217 if ( j == globalImages->images.Num() ) {
1222 // "listimages touched" will list only images bound since the last "listimages touched" call
1224 if ( image->bindCount == 0 ) {
1227 image->bindCount = 0;
1231 sortedArray[count].image = image;
1232 sortedArray[count].size = image->StorageSize();
1234 common->Printf( "%4i:", i );
1237 totalSize += image->StorageSize();
1242 qsort( sortedArray, count, sizeof( sortedImage_t ), R_QsortImageSizes );
1244 for ( i = 0 ; i < count ; i++ ) {
1245 common->Printf( "%4i:", i );
1246 sortedArray[i].image->Print();
1247 partialSize += sortedArray[i].image->StorageSize();
1248 if ( ( (i+1) % 10 ) == 0 ) {
1249 common->Printf( "-------- %5.1f of %5.1f megs --------\n",
1250 partialSize / (1024*1024.0), totalSize / (1024*1024.0) );
1255 common->Printf( "%s", header );
1256 common->Printf( " %i images (%i total)\n", count, globalImages->images.Num() );
1257 common->Printf( " %5.1f total megabytes of images\n\n\n", totalSize / (1024*1024.0) );
1259 if ( byClassification ) {
1261 idList< int > classifications[IC_COUNT];
1263 for ( i = 0 ; i < count ; i++ ) {
1264 int cl = ClassifyImage( sortedArray[i].image->imgName );
1265 classifications[ cl ].Append( i );
1268 for ( i = 0; i < IC_COUNT; i++ ) {
1270 idList< int > overSizedList;
1271 for ( j = 0; j < classifications[ i ].Num(); j++ ) {
1272 partialSize += sortedArray[ classifications[ i ][ j ] ].image->StorageSize();
1274 if ( sortedArray[ classifications[ i ][ j ] ].image->uploadWidth > IC_Info[i].maxWidth && sortedArray[ classifications[ i ][ j ] ].image->uploadHeight > IC_Info[i].maxHeight ) {
1275 overSizedList.Append( classifications[ i ][ j ] );
1279 common->Printf ( " Classification %s contains %i images using %5.1f megabytes\n", IC_Info[i].desc, classifications[i].Num(), partialSize / ( 1024*1024.0 ) );
1280 if ( overSized && overSizedList.Num() ) {
1281 common->Printf( " The following images may be oversized\n" );
1282 for ( j = 0; j < overSizedList.Num(); j++ ) {
1283 common->Printf( " " );
1284 sortedArray[ overSizedList[ j ] ].image->Print();
1285 common->Printf( "\n" );
1297 Create a 256 color palette to be used by compressed normal maps
1300 void idImageManager::SetNormalPalette( void ) {
1304 //byte temptable[768];
1305 byte *temptable = compressedPalette;
1306 int compressedToOriginal[16];
1308 // make an ad-hoc separable compression mapping scheme
1309 for ( i = 0 ; i < 8 ; i++ ) {
1312 f = ( i + 1 ) / 8.5;
1313 y = idMath::Sqrt( 1.0 - f * f );
1316 compressedToOriginal[7-i] = 127 - (int)( y * 127 + 0.5 );
1317 compressedToOriginal[8+i] = 128 + (int)( y * 127 + 0.5 );
1320 for ( i = 0 ; i < 256 ; i++ ) {
1321 if ( i <= compressedToOriginal[0] ) {
1322 originalToCompressed[i] = 0;
1323 } else if ( i >= compressedToOriginal[15] ) {
1324 originalToCompressed[i] = 15;
1326 for ( j = 0 ; j < 14 ; j++ ) {
1327 if ( i <= compressedToOriginal[j+1] ) {
1331 if ( i - compressedToOriginal[j] < compressedToOriginal[j+1] - i ) {
1332 originalToCompressed[i] = j;
1334 originalToCompressed[i] = j + 1;
1340 for ( i = 0; i < 16; i++ ) {
1341 for ( j = 0 ; j < 16 ; j++ ) {
1343 v[0] = ( i - 7.5 ) / 8;
1344 v[1] = ( j - 7.5 ) / 8;
1346 t = 1.0 - ( v[0]*v[0] + v[1]*v[1] );
1350 v[2] = idMath::Sqrt( t );
1352 temptable[(i*16+j)*3+0] = 128 + floor( 127 * v[0] + 0.5 );
1353 temptable[(i*16+j)*3+1] = 128 + floor( 127 * v[1] );
1354 temptable[(i*16+j)*3+2] = 128 + floor( 127 * v[2] );
1358 for ( i = 0; i < 16; i++ ) {
1359 for ( j = 0 ; j < 16 ; j++ ) {
1361 v[0] = ( compressedToOriginal[i] - 127.5 ) / 128;
1362 v[1] = ( compressedToOriginal[j] - 127.5 ) / 128;
1364 t = 1.0 - ( v[0]*v[0] + v[1]*v[1] );
1368 v[2] = idMath::Sqrt( t );
1370 temptable[(i*16+j)*3+0] = (byte)(128 + floor( 127 * v[0] + 0.5 ));
1371 temptable[(i*16+j)*3+1] = (byte)(128 + floor( 127 * v[1] ));
1372 temptable[(i*16+j)*3+2] = (byte)(128 + floor( 127 * v[2] ));
1377 // color 255 will be the "nullnormal" color for no reflection
1378 temptable[255*3+0] =
1379 temptable[255*3+1] =
1380 temptable[255*3+2] = 128;
1382 if ( !glConfig.sharedTexturePaletteAvailable ) {
1386 qglColorTableEXT( GL_SHARED_TEXTURE_PALETTE_EXT,
1393 qglEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
1400 Allocates an idImage, adds it to the list,
1401 copies the name, and adds it to the hash chain.
1404 idImage *idImageManager::AllocImage( const char *name ) {
1408 if (strlen(name) >= MAX_IMAGE_NAME ) {
1409 common->Error ("idImageManager::AllocImage: \"%s\" is too long\n", name);
1412 hash = idStr( name ).FileNameHash();
1414 image = new idImage;
1415 images.Append( image );
1417 image->hashNext = imageHashTable[hash];
1418 imageHashTable[hash] = image;
1420 image->imgName = name;
1429 Images that are procedurally generated are allways specified
1430 with a callback which must work at any time, allowing the OpenGL
1431 system to be completely regenerated if needed.
1434 idImage *idImageManager::ImageFromFunction( const char *_name, void (*generatorFunction)( idImage *image ) ) {
1440 common->FatalError( "idImageManager::ImageFromFunction: NULL name" );
1443 // strip any .tga file extensions from anywhere in the _name
1445 name.Replace( ".tga", "" );
1446 name.BackSlashesToSlashes();
1448 // see if the image already exists
1449 hash = name.FileNameHash();
1450 for ( image = imageHashTable[hash] ; image; image = image->hashNext ) {
1451 if ( name.Icmp( image->imgName ) == 0 ) {
1452 if ( image->generatorFunction != generatorFunction ) {
1453 common->DPrintf( "WARNING: reused image %s with mixed generators\n", name.c_str() );
1459 // create the image and issue the callback
1460 image = AllocImage( name );
1462 image->generatorFunction = generatorFunction;
1464 if ( image_preload.GetBool() ) {
1465 // check for precompressed, load is from the front end
1466 image->referencedOutsideLevelLoad = true;
1467 image->ActuallyLoadImage( true, false );
1477 Finds or loads the given image, always returning a valid image pointer.
1478 Loading of the image may be deferred for dynamic loading.
1481 idImage *idImageManager::ImageFromFile( const char *_name, textureFilter_t filter, bool allowDownSize,
1482 textureRepeat_t repeat, textureDepth_t depth, cubeFiles_t cubeMap ) {
1487 if ( !_name || !_name[0] || idStr::Icmp( _name, "default" ) == 0 || idStr::Icmp( _name, "_default" ) == 0 ) {
1488 declManager->MediaPrint( "DEFAULTED\n" );
1489 return globalImages->defaultImage;
1492 // strip any .tga file extensions from anywhere in the _name, including image program parameters
1494 name.Replace( ".tga", "" );
1495 name.BackSlashesToSlashes();
1498 // see if the image is already loaded, unless we
1499 // are in a reloadImages call
1501 hash = name.FileNameHash();
1502 for ( image = imageHashTable[hash]; image; image = image->hashNext ) {
1503 if ( name.Icmp( image->imgName ) == 0 ) {
1504 // the built in's, like _white and _flat always match the other options
1505 if ( name[0] == '_' ) {
1508 if ( image->cubeFiles != cubeMap ) {
1509 common->Error( "Image '%s' has been referenced with conflicting cube map states", _name );
1512 if ( image->filter != filter || image->repeat != repeat ) {
1513 // we might want to have the system reset these parameters on every bind and
1514 // share the image data
1518 if ( image->allowDownSize == allowDownSize && image->depth == depth ) {
1519 // note that it is used this level load
1520 image->levelLoadReferenced = true;
1521 if ( image->partialImage != NULL ) {
1522 image->partialImage->levelLoadReferenced = true;
1527 // the same image is being requested, but with a different allowDownSize or depth
1528 // so pick the highest of the two and reload the old image with those parameters
1529 if ( !image->allowDownSize ) {
1530 allowDownSize = false;
1532 if ( image->depth > depth ) {
1533 depth = image->depth;
1535 if ( image->allowDownSize == allowDownSize && image->depth == depth ) {
1536 // the already created one is already the highest quality
1537 image->levelLoadReferenced = true;
1538 if ( image->partialImage != NULL ) {
1539 image->partialImage->levelLoadReferenced = true;
1544 image->allowDownSize = allowDownSize;
1545 image->depth = depth;
1546 image->levelLoadReferenced = true;
1547 if ( image->partialImage != NULL ) {
1548 image->partialImage->levelLoadReferenced = true;
1550 if ( image_preload.GetBool() && !insideLevelLoad ) {
1551 image->referencedOutsideLevelLoad = true;
1552 image->ActuallyLoadImage( true, false ); // check for precompressed, load is from front end
1553 declManager->MediaPrint( "%ix%i %s (reload for mixed referneces)\n", image->uploadWidth, image->uploadHeight, image->imgName.c_str() );
1560 // create a new image
1562 image = AllocImage( name );
1564 // HACK: to allow keep fonts from being mip'd, as new ones will be introduced with localization
1565 // this keeps us from having to make a material for each font tga
1566 if ( name.Find( "fontImage_") >= 0 ) {
1567 allowDownSize = false;
1570 image->allowDownSize = allowDownSize;
1571 image->repeat = repeat;
1572 image->depth = depth;
1573 image->type = TT_2D;
1574 image->cubeFiles = cubeMap;
1575 image->filter = filter;
1577 image->levelLoadReferenced = true;
1579 // also create a shrunken version if we are going to dynamically cache the full size image
1580 if ( image->ShouldImageBePartialCached() ) {
1581 // if we only loaded part of the file, create a new idImage for the shrunken version
1582 image->partialImage = new idImage;
1584 image->partialImage->allowDownSize = allowDownSize;
1585 image->partialImage->repeat = repeat;
1586 image->partialImage->depth = depth;
1587 image->partialImage->type = TT_2D;
1588 image->partialImage->cubeFiles = cubeMap;
1589 image->partialImage->filter = filter;
1591 image->partialImage->levelLoadReferenced = true;
1593 // we don't bother hooking this into the hash table for lookup, but we do add it to the manager
1594 // list for listImages
1595 globalImages->images.Append( image->partialImage );
1596 image->partialImage->imgName = image->imgName;
1597 image->partialImage->isPartialImage = true;
1599 // let the background file loader know that we can load
1600 image->precompressedFile = true;
1602 if ( image_preload.GetBool() && !insideLevelLoad ) {
1603 image->partialImage->ActuallyLoadImage( true, false ); // check for precompressed, load is from front end
1604 declManager->MediaPrint( "%ix%i %s\n", image->partialImage->uploadWidth, image->partialImage->uploadHeight, image->imgName.c_str() );
1606 declManager->MediaPrint( "%s\n", image->imgName.c_str() );
1611 // load it if we aren't in a level preload
1612 if ( image_preload.GetBool() && !insideLevelLoad ) {
1613 image->referencedOutsideLevelLoad = true;
1614 image->ActuallyLoadImage( true, false ); // check for precompressed, load is from front end
1615 declManager->MediaPrint( "%ix%i %s\n", image->uploadWidth, image->uploadHeight, image->imgName.c_str() );
1617 declManager->MediaPrint( "%s\n", image->imgName.c_str() );
1625 idImageManager::GetImage
1628 idImage *idImageManager::GetImage( const char *_name ) const {
1633 if ( !_name || !_name[0] || idStr::Icmp( _name, "default" ) == 0 || idStr::Icmp( _name, "_default" ) == 0 ) {
1634 declManager->MediaPrint( "DEFAULTED\n" );
1635 return globalImages->defaultImage;
1638 // strip any .tga file extensions from anywhere in the _name, including image program parameters
1640 name.Replace( ".tga", "" );
1641 name.BackSlashesToSlashes();
1644 // look in loaded images
1646 hash = name.FileNameHash();
1647 for ( image = imageHashTable[hash]; image; image = image->hashNext ) {
1648 if ( name.Icmp( image->imgName ) == 0 ) {
1661 void idImageManager::PurgeAllImages() {
1665 for ( i = 0; i < images.Num() ; i++ ) {
1667 image->PurgeImage();
1676 void idImageManager::ReloadAllImages() {
1679 // build the compressed normal map palette
1682 args.TokenizeString( "reloadImages reload", false );
1683 R_ReloadImages_f( args );
1688 R_CombineCubeImages_f
1690 Used to combine animations of six separate tga files into
1691 a serials of 6x taller tga files, for preparation to roq compress
1694 void R_CombineCubeImages_f( const idCmdArgs &args ) {
1695 if ( args.Argc() != 2 ) {
1696 common->Printf( "usage: combineCubeImages <baseName>\n" );
1697 common->Printf( " combines basename[1-6][0001-9999].tga to basenameCM[0001-9999].tga\n" );
1698 common->Printf( " 1: forward 2:right 3:back 4:left 5:up 6:down\n" );
1702 idStr baseName = args.Argv( 1 );
1703 common->SetRefreshOnPrint( true );
1705 for ( int frameNum = 1 ; frameNum < 10000 ; frameNum++ ) {
1706 char filename[MAX_IMAGE_NAME];
1710 int orderRemap[6] = { 1,3,4,2,5,6 };
1711 for ( side = 0 ; side < 6 ; side++ ) {
1712 sprintf( filename, "%s%i%04i.tga", baseName.c_str(), orderRemap[side], frameNum );
1714 common->Printf( "reading %s\n", filename );
1715 R_LoadImage( filename, &pics[side], &width, &height, NULL, true );
1717 if ( !pics[side] ) {
1718 common->Printf( "not found.\n" );
1722 // convert from "camera" images to native cube map images
1725 R_RotatePic( pics[side], width);
1728 R_RotatePic( pics[side], width);
1729 R_HorizontalFlip( pics[side], width, height );
1730 R_VerticalFlip( pics[side], width, height );
1733 R_VerticalFlip( pics[side], width, height );
1736 R_HorizontalFlip( pics[side], width, height );
1739 R_RotatePic( pics[side], width);
1742 R_RotatePic( pics[side], width);
1748 for ( int i = 0 ; i < side ; side++ ) {
1749 Mem_Free( pics[side] );
1754 byte *combined = (byte *)Mem_Alloc( width*height*6*4 );
1755 for ( side = 0 ; side < 6 ; side++ ) {
1756 memcpy( combined+width*height*4*side, pics[side], width*height*4 );
1757 Mem_Free( pics[side] );
1759 sprintf( filename, "%sCM%04i.tga", baseName.c_str(), frameNum );
1761 common->Printf( "writing %s\n", filename );
1762 R_WriteTGA( filename, combined, width, height*6 );
1764 Mem_Free( combined );
1766 common->SetRefreshOnPrint( false );
1772 idImage::StartBackgroundImageLoad
1775 void idImage::StartBackgroundImageLoad() {
1776 if ( imageManager.numActiveBackgroundImageLoads >= idImageManager::MAX_BACKGROUND_IMAGE_LOADS ) {
1779 if ( globalImages->image_showBackgroundLoads.GetBool() ) {
1780 common->Printf( "idImage::StartBackgroundImageLoad: %s\n", imgName.c_str() );
1782 backgroundLoadInProgress = true;
1784 if ( !precompressedFile ) {
1785 common->Warning( "idImageManager::StartBackgroundImageLoad: %s wasn't a precompressed file", imgName.c_str() );
1789 bglNext = globalImages->backgroundImageLoads;
1790 globalImages->backgroundImageLoads = this;
1792 char filename[MAX_IMAGE_NAME];
1793 ImageProgramStringToCompressedFileName( imgName, filename );
1795 bgl.completed = false;
1796 bgl.f = fileSystem->OpenFileRead( filename );
1798 common->Warning( "idImageManager::StartBackgroundImageLoad: Couldn't load %s", imgName.c_str() );
1801 bgl.file.position = 0;
1802 bgl.file.length = bgl.f->Length();
1803 if ( bgl.file.length < sizeof( ddsFileHeader_t ) ) {
1804 common->Warning( "idImageManager::StartBackgroundImageLoad: %s had a bad file length", imgName.c_str() );
1808 bgl.file.buffer = R_StaticAlloc( bgl.file.length );
1810 fileSystem->BackgroundDownload( &bgl );
1812 imageManager.numActiveBackgroundImageLoads++;
1814 // purge some images if necessary
1816 for ( idImage *check = globalImages->cacheLRU.cacheUsageNext ; check != &globalImages->cacheLRU ; check = check->cacheUsageNext ) {
1817 totalSize += check->StorageSize();
1819 int needed = this->StorageSize();
1821 while ( ( totalSize + needed ) > globalImages->image_cacheMegs.GetFloat() * 1024 * 1024 ) {
1822 // purge the least recently used
1823 idImage *check = globalImages->cacheLRU.cacheUsagePrev;
1824 if ( check->texnum != TEXTURE_NOT_LOADED ) {
1825 totalSize -= check->StorageSize();
1826 if ( globalImages->image_showBackgroundLoads.GetBool() ) {
1827 common->Printf( "purging %s\n", check->imgName.c_str() );
1829 check->PurgeImage();
1831 // remove it from the cached list
1832 check->cacheUsageNext->cacheUsagePrev = check->cacheUsagePrev;
1833 check->cacheUsagePrev->cacheUsageNext = check->cacheUsageNext;
1834 check->cacheUsageNext = NULL;
1835 check->cacheUsagePrev = NULL;
1841 R_CompleteBackgroundImageLoads
1843 Do we need to worry about vid_restarts here?
1846 void idImageManager::CompleteBackgroundImageLoads() {
1847 idImage *remainingList = NULL;
1850 for ( idImage *image = backgroundImageLoads ; image ; image = next ) {
1851 next = image->bglNext;
1852 if ( image->bgl.completed ) {
1853 numActiveBackgroundImageLoads--;
1854 fileSystem->CloseFile( image->bgl.f );
1856 image->UploadPrecompressedImage( (byte *)image->bgl.file.buffer, image->bgl.file.length );
1857 R_StaticFree( image->bgl.file.buffer );
1858 if ( image_showBackgroundLoads.GetBool() ) {
1859 common->Printf( "R_CompleteBackgroundImageLoad: %s\n", image->imgName.c_str() );
1862 image->bglNext = remainingList;
1863 remainingList = image;
1866 if ( image_showBackgroundLoads.GetBool() ) {
1868 if ( numActiveBackgroundImageLoads != prev ) {
1869 prev = numActiveBackgroundImageLoads;
1870 common->Printf( "background Loads: %i\n", numActiveBackgroundImageLoads );
1874 backgroundImageLoads = remainingList;
1882 void idImageManager::CheckCvars() {
1883 // textureFilter stuff
1884 if ( image_filter.IsModified() || image_anisotropy.IsModified() || image_lodbias.IsModified() ) {
1885 ChangeTextureFilter();
1886 image_filter.ClearModified();
1887 image_anisotropy.ClearModified();
1888 image_lodbias.ClearModified();
1897 int idImageManager::SumOfUsedImages() {
1903 for ( i = 0; i < images.Num(); i++ ) {
1905 if ( image->frameUsed == backEnd.frameCount ) {
1906 total += image->StorageSize();
1918 void idImageManager::BindNull() {
1921 tmu = &backEnd.glState.tmu[backEnd.glState.currenttmu];
1923 RB_LogComment( "BindNull()\n" );
1924 if ( tmu->textureType == TT_CUBIC ) {
1925 qglDisable( GL_TEXTURE_CUBE_MAP_EXT );
1926 } else if ( tmu->textureType == TT_3D ) {
1927 qglDisable( GL_TEXTURE_3D );
1928 } else if ( tmu->textureType == TT_2D ) {
1929 qglDisable( GL_TEXTURE_2D );
1931 tmu->textureType = TT_DISABLED;
1939 void idImageManager::Init() {
1941 memset(imageHashTable, 0, sizeof(imageHashTable));
1943 images.Resize( 1024, 1024 );
1945 // clear the cached LRU
1946 cacheLRU.cacheUsageNext = &cacheLRU;
1947 cacheLRU.cacheUsagePrev = &cacheLRU;
1949 // set default texture filter modes
1950 ChangeTextureFilter();
1952 // create built in images
1953 defaultImage = ImageFromFunction( "_default", R_DefaultImage );
1954 whiteImage = ImageFromFunction( "_white", R_WhiteImage );
1955 blackImage = ImageFromFunction( "_black", R_BlackImage );
1956 borderClampImage = ImageFromFunction( "_borderClamp", R_BorderClampImage );
1957 flatNormalMap = ImageFromFunction( "_flat", R_FlatNormalImage );
1958 ambientNormalMap = ImageFromFunction( "_ambient", R_AmbientNormalImage );
1959 specularTableImage = ImageFromFunction( "_specularTable", R_SpecularTableImage );
1960 specular2DTableImage = ImageFromFunction( "_specular2DTable", R_Specular2DTableImage );
1961 rampImage = ImageFromFunction( "_ramp", R_RampImage );
1962 alphaRampImage = ImageFromFunction( "_alphaRamp", R_RampImage );
1963 alphaNotchImage = ImageFromFunction( "_alphaNotch", R_AlphaNotchImage );
1964 fogImage = ImageFromFunction( "_fog", R_FogImage );
1965 fogEnterImage = ImageFromFunction( "_fogEnter", R_FogEnterImage );
1966 normalCubeMapImage = ImageFromFunction( "_normalCubeMap", makeNormalizeVectorCubeMap );
1967 noFalloffImage = ImageFromFunction( "_noFalloff", R_CreateNoFalloffImage );
1968 ImageFromFunction( "_quadratic", R_QuadraticImage );
1970 // cinematicImage is used for cinematic drawing
1971 // scratchImage is used for screen wipes/doublevision etc..
1972 cinematicImage = ImageFromFunction("_cinematic", R_RGBA8Image );
1973 scratchImage = ImageFromFunction("_scratch", R_RGBA8Image );
1974 scratchImage2 = ImageFromFunction("_scratch2", R_RGBA8Image );
1975 accumImage = ImageFromFunction("_accum", R_RGBA8Image );
1976 scratchCubeMapImage = ImageFromFunction("_scratchCubeMap", makeNormalizeVectorCubeMap );
1977 currentRenderImage = ImageFromFunction("_currentRender", R_RGBA8Image );
1979 cmdSystem->AddCommand( "reloadImages", R_ReloadImages_f, CMD_FL_RENDERER, "reloads images" );
1980 cmdSystem->AddCommand( "listImages", R_ListImages_f, CMD_FL_RENDERER, "lists images" );
1981 cmdSystem->AddCommand( "combineCubeImages", R_CombineCubeImages_f, CMD_FL_RENDERER, "combines six images for roq compression" );
1983 // should forceLoadImages be here?
1991 void idImageManager::Shutdown() {
1992 images.DeleteContents( true );
1996 ====================
1999 Mark all file based images as currently unused,
2000 but don't free anything. Calls to ImageFromFile() will
2001 either mark the image as used, or create a new image without
2002 loading the actual data.
2003 ====================
2005 void idImageManager::BeginLevelLoad() {
2006 insideLevelLoad = true;
2008 for ( int i = 0 ; i < images.Num() ; i++ ) {
2009 idImage *image = images[ i ];
2011 // generator function images are always kept around
2012 if ( image->generatorFunction ) {
2016 if ( com_purgeAll.GetBool() ) {
2017 image->PurgeImage();
2020 image->levelLoadReferenced = false;
2025 ====================
2028 Free all images marked as unused, and load all images that are necessary.
2029 This architecture prevents us from having the union of two level's
2030 worth of data present at one time.
2032 preload everything, never free
2033 preload everything, free unused after level load
2034 blocking load on demand
2035 preload low mip levels, background load remainder on demand
2036 ====================
2038 void idImageManager::EndLevelLoad() {
2039 int start = Sys_Milliseconds();
2041 insideLevelLoad = false;
2042 if ( idAsyncNetwork::serverDedicated.GetInteger() ) {
2046 common->Printf( "----- idImageManager::EndLevelLoad -----\n" );
2052 // purge the ones we don't need
2053 for ( int i = 0 ; i < images.Num() ; i++ ) {
2054 idImage *image = images[ i ];
2055 if ( image->generatorFunction ) {
2059 if ( !image->levelLoadReferenced && !image->referencedOutsideLevelLoad ) {
2060 // common->Printf( "Purging %s\n", image->imgName.c_str() );
2062 image->PurgeImage();
2063 } else if ( image->texnum != idImage::TEXTURE_NOT_LOADED ) {
2064 // common->Printf( "Keeping %s\n", image->imgName.c_str() );
2069 // load the ones we do need, if we are preloading
2070 for ( int i = 0 ; i < images.Num() ; i++ ) {
2071 idImage *image = images[ i ];
2072 if ( image->generatorFunction ) {
2076 if ( image->levelLoadReferenced && image->texnum == idImage::TEXTURE_NOT_LOADED && !image->partialImage ) {
2077 // common->Printf( "Loading %s\n", image->imgName.c_str() );
2079 image->ActuallyLoadImage( true, false );
2081 if ( ( loadCount & 15 ) == 0 ) {
2082 session->PacifierUpdate();
2087 int end = Sys_Milliseconds();
2088 common->Printf( "%5i purged from previous\n", purgeCount );
2089 common->Printf( "%5i kept from previous\n", keepCount );
2090 common->Printf( "%5i new loaded\n", loadCount );
2091 common->Printf( "all images loaded in %5.1f seconds\n", (end-start) * 0.001 );
2092 common->Printf( "----------------------------------------\n" );
2097 idImageManager::StartBuild
2100 void idImageManager::StartBuild() {
2107 idImageManager::FinishBuild
2110 void idImageManager::FinishBuild( bool removeDups ) {
2114 char *buffer = NULL;
2115 fileSystem->ReadFile( "makedds.bat", (void**)&buffer );
2118 while ( str.Length() ) {
2119 int n = str.Find( '\n' );
2121 idStr line = str.Left( n + 1 );
2123 str.Right( str.Length() - n - 1, right );
2125 ddsList.AddUnique( line );
2132 batchFile = fileSystem->OpenFileWrite( ( removeDups ) ? "makedds2.bat" : "makedds.bat" );
2135 int ddsNum = ddsList.Num();
2137 for ( i = 0; i < ddsNum; i++ ) {
2138 batchFile->WriteFloatString( "%s", ddsList[ i ].c_str() );
2139 batchFile->Printf( "@echo Finished compressing %d of %d. %.1f percent done.\n", i+1, ddsNum, ((float)(i+1)/(float)ddsNum)*100.f );
2141 fileSystem->CloseFile( batchFile );
2149 idImageManager::AddDDSCommand
2152 void idImageManager::AddDDSCommand( const char *cmd ) {
2155 if ( !( cmd && *cmd ) ) {
2159 key = ddsHash.GenerateKey( cmd, false );
2160 for ( i = ddsHash.First( key ); i != -1; i = ddsHash.Next( i ) ) {
2161 if ( ddsList[i].Icmp( cmd ) == 0 ) {
2167 ddsList.Append( cmd );
2173 idImageManager::PrintMemInfo
2176 void idImageManager::PrintMemInfo( MemInfo_t *mi ) {
2177 int i, j, total = 0;
2181 f = fileSystem->OpenFileWrite( mi->filebase + "_images.txt" );
2187 sortIndex = new int[images.Num()];
2189 for ( i = 0; i < images.Num(); i++ ) {
2193 for ( i = 0; i < images.Num() - 1; i++ ) {
2194 for ( j = i + 1; j < images.Num(); j++ ) {
2195 if ( images[sortIndex[i]]->StorageSize() < images[sortIndex[j]]->StorageSize() ) {
2196 int temp = sortIndex[i];
2197 sortIndex[i] = sortIndex[j];
2198 sortIndex[j] = temp;
2204 for ( i = 0; i < images.Num(); i++ ) {
2205 idImage *im = images[sortIndex[i]];
2208 size = im->StorageSize();
2211 f->Printf( "%s %3i %s\n", idStr::FormatNumber( size ).c_str(), im->refCount, im->imgName.c_str() );
2215 mi->imageAssetsTotal = total;
2217 f->Printf( "\nTotal image bytes allocated: %s\n", idStr::FormatNumber( total ).c_str() );
2218 fileSystem->CloseFile( f );