]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Image_init.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / renderer / Image_init.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "tr_local.h"
33
34 const char *imageFilter[] = {
35         "GL_LINEAR_MIPMAP_NEAREST",
36         "GL_LINEAR_MIPMAP_LINEAR",
37         "GL_NEAREST",
38         "GL_LINEAR",
39         "GL_NEAREST_MIPMAP_NEAREST",
40         "GL_NEAREST_MIPMAP_LINEAR",
41         NULL
42 };
43
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;
75
76
77 enum IMAGE_CLASSIFICATION {
78         IC_NPC,
79         IC_WEAPON,
80         IC_MONSTER,
81         IC_MODELGEOMETRY,
82         IC_ITEMS,
83         IC_MODELSOTHER,
84         IC_GUIS,
85         IC_WORLDGEOMETRY,
86         IC_OTHER,
87         IC_COUNT
88 };
89
90 struct imageClassificate_t {
91         const char *rootPath;
92         const char *desc;
93         int type;
94         int maxWidth;
95         int maxHeight;
96 };
97
98 typedef idList< int > intList;
99
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 }
110 };
111
112
113
114 static int ClassifyImage( const char *name ) {
115         idStr str;
116         str = 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;
120                 }
121         }
122         return IC_OTHER;
123 }
124
125 /*
126 ================
127 R_RampImage
128
129 Creates a 0-255 ramp image
130 ================
131 */
132 static void R_RampImage( idImage *image ) {
133         int             x;
134         byte    data[256][4];
135
136         for (x=0 ; x<256 ; x++) {
137                 data[x][0] = 
138                 data[x][1] = 
139                 data[x][2] = 
140                 data[x][3] = x;                 
141         }
142
143         image->GenerateImage( (byte *)data, 256, 1, 
144                 TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
145 }
146
147 /*
148 ================
149 R_SpecularTableImage
150
151 Creates a ramp that matches our fudged specular calculation
152 ================
153 */
154 static void R_SpecularTableImage( idImage *image ) {
155         int             x;
156         byte    data[256][4];
157
158         for (x=0 ; x<256 ; x++) {
159                 float f = x/255.f;
160 #if 0
161                 f = pow(f, 16);
162 #else
163                 // this is the behavior of the hacked up fragment programs that
164                 // can't really do a power function
165                 f = (f-0.75)*4;
166                 if ( f < 0 ) {
167                         f = 0;
168                 }
169                 f = f * f;
170 #endif
171                 int             b = (int)(f * 255);
172
173                 data[x][0] = 
174                 data[x][1] = 
175                 data[x][2] = 
176                 data[x][3] = b;
177         }
178
179         image->GenerateImage( (byte *)data, 256, 1, 
180                 TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
181 }
182
183
184 /*
185 ================
186 R_Specular2DTableImage
187
188 Create a 2D table that calculates ( reflection dot , specularity )
189 ================
190 */
191 static void R_Specular2DTableImage( idImage *image ) {
192         int             x, y;
193         byte    data[256][256][4];
194
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++ ) {
199
200                         int b = (int)( pow( f, y ) * 255.0f );
201                         if ( b == 0 ) {
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
204                                 break;
205                         }
206
207                         data[y][x][0] = 
208                         data[y][x][1] = 
209                         data[y][x][2] = 
210                         data[y][x][3] = b;
211                 }
212         }
213
214         image->GenerateImage( (byte *)data, 256, 256, TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
215 }
216
217
218
219 /*
220 ================
221 R_AlphaRampImage
222
223 Creates a 0-255 ramp image
224 ================
225 */
226 static void R_AlphaRampImage( idImage *image ) {
227         int             x;
228         byte    data[256][4];
229
230         for (x=0 ; x<256 ; x++) {
231                 data[x][0] = 
232                 data[x][1] = 
233                 data[x][2] = 255;
234                 data[x][3] = x;                 
235         }
236
237         image->GenerateImage( (byte *)data, 256, 1, 
238                 TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
239 }
240
241
242
243 /*
244 ==================
245 R_CreateDefaultImage
246
247 the default image will be grey with a white box outline
248 to allow you to see the mapping coordinates on a surface
249 ==================
250 */
251 #define DEFAULT_SIZE    16
252 void idImage::MakeDefault() {
253         int             x, y;
254         byte    data[DEFAULT_SIZE][DEFAULT_SIZE][4];
255
256         if ( com_developer.GetBool() ) {
257                 // grey center
258                 for ( y = 0 ; y < DEFAULT_SIZE ; y++ ) {
259                         for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
260                                 data[y][x][0] = 32;
261                                 data[y][x][1] = 32;
262                                 data[y][x][2] = 32;
263                                 data[y][x][3] = 255;
264                         }
265                 }
266
267                 // white border
268                 for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
269                         data[0][x][0] =
270                                 data[0][x][1] =
271                                 data[0][x][2] =
272                                 data[0][x][3] = 255;
273
274                         data[x][0][0] =
275                                 data[x][0][1] =
276                                 data[x][0][2] =
277                                 data[x][0][3] = 255;
278
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;
283
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;
288                 }
289         } else {
290                 for ( y = 0 ; y < DEFAULT_SIZE ; y++ ) {
291                         for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
292                                 data[y][x][0] = 0;
293                                 data[y][x][1] = 0;
294                                 data[y][x][2] = 0;
295                                 data[y][x][3] = 0;
296                         }
297                 }
298         }
299
300         GenerateImage( (byte *)data, 
301                 DEFAULT_SIZE, DEFAULT_SIZE, 
302                 TF_DEFAULT, true, TR_REPEAT, TD_DEFAULT );
303
304         defaulted = true;
305 }
306
307 static void R_DefaultImage( idImage *image ) {
308         image->MakeDefault();
309 }
310
311 static void R_WhiteImage( idImage *image ) {
312         byte    data[DEFAULT_SIZE][DEFAULT_SIZE][4];
313
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 );
318 }
319
320 static void R_BlackImage( idImage *image ) {
321         byte    data[DEFAULT_SIZE][DEFAULT_SIZE][4];
322
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 );
327 }
328
329
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];
334
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++ ) {
338                 data[i][0][0] = 
339                 data[i][0][1] = 
340                 data[i][0][2] = 
341                 data[i][0][3] = 
342
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] = 
347
348                 data[0][i][0] = 
349                 data[0][i][1] = 
350                 data[0][i][2] = 
351                 data[0][i][3] = 
352
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;
357         }
358
359         image->GenerateImage( (byte *)data, BORDER_CLAMP_SIZE, BORDER_CLAMP_SIZE, 
360                 TF_LINEAR /* TF_NEAREST */, false, TR_CLAMP_TO_BORDER, TD_DEFAULT );
361
362         if ( !glConfig.isInitialized ) {
363                 // can't call qglTexParameterfv yet
364                 return;
365         }
366         // explicit zero border
367         float   color[4];
368         color[0] = color[1] = color[2] = color[3] = 0;
369         qglTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color );
370 }
371
372 static void R_RGBA8Image( idImage *image ) {
373         byte    data[DEFAULT_SIZE][DEFAULT_SIZE][4];
374
375         memset( data, 0, sizeof( data ) );
376         data[0][0][0] = 16;
377         data[0][0][1] = 32;
378         data[0][0][2] = 48;
379         data[0][0][3] = 96;
380
381         image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, 
382                 TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
383 }
384
385 static void R_RGB8Image( idImage *image ) {
386         byte    data[DEFAULT_SIZE][DEFAULT_SIZE][4];
387
388         memset( data, 0, sizeof( data ) );
389         data[0][0][0] = 16;
390         data[0][0][1] = 32;
391         data[0][0][2] = 48;
392         data[0][0][3] = 255;
393
394         image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, 
395                 TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
396 }
397
398 static void R_AlphaNotchImage( idImage *image ) {
399         byte    data[2][4];
400
401         // this is used for alpha test clip planes
402
403         data[0][0] = data[0][1] = data[0][2] = 255;
404         data[0][3] = 0;
405         data[1][0] = data[1][1] = data[1][2] = 255;
406         data[1][3] = 255;
407
408         image->GenerateImage( (byte *)data, 2, 1, 
409                 TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
410 }
411
412 static void R_FlatNormalImage( idImage *image ) {
413         byte    data[DEFAULT_SIZE][DEFAULT_SIZE][4];
414         int             i;
415
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;
421                 data[0][i][1] = 128;
422                 data[0][i][2] = 255;
423                 data[0][i][alpha] = 255;
424         }
425         image->GenerateImage( (byte *)data, 2, 2, 
426                 TF_DEFAULT, true, TR_REPEAT, TD_HIGH_QUALITY );
427 }
428
429 static void R_AmbientNormalImage( idImage *image ) {
430         byte    data[DEFAULT_SIZE][DEFAULT_SIZE][4];
431         int             i;
432
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;
441         }
442         const byte      *pics[6];
443         for ( i = 0 ; i < 6 ; i++ ) {
444                 pics[i] = data[0][0];
445         }
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 );
448 }
449
450
451 static void CreateSquareLight( void ) {
452         byte            *buffer;
453         int                     x, y;
454         int                     dx, dy;
455         int                     d;
456         int                     width, height;
457
458         width = height = 128;
459
460         buffer = (byte *)R_StaticAlloc( 128 * 128 * 4 );
461
462         for ( x = 0 ; x < 128 ; x++ ) {
463                 if ( x < 32 ) {
464                         dx = 32 - x;
465                 } else if ( x > 96 ) {
466                         dx = x - 96;
467                 } else {
468                         dx = 0;
469                 }
470                 for ( y = 0 ; y < 128 ; y++ ) {
471                         if ( y < 32 ) {
472                                 dy = 32 - y;
473                         } else if ( y > 96 ) {
474                                 dy = y - 96;
475                         } else {
476                                 dy = 0;
477                         }
478                         d = (byte)idMath::Sqrt( dx * dx + dy * dy );
479                         if ( d > 32 ) {
480                                 d = 32;
481                         }
482                         d = 255 - d * 8;
483                         if ( d < 0 ) {
484                                 d = 0;
485                         }
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;
490                 }
491         }
492
493         R_WriteTGA( "lights/squarelight.tga", buffer, width, height );
494
495         R_StaticFree( buffer );
496 }
497
498 static void CreateFlashOff( void ) {
499         byte            *buffer;
500         int                     x, y;
501         int                     d;
502         int                     width, height;
503
504         width = 256;
505         height = 4;
506
507         buffer = (byte *)R_StaticAlloc( width * height * 4 );
508
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;
516                 }
517         }
518
519         R_WriteTGA( "lights/flashoff.tga", buffer, width, height );
520
521         R_StaticFree( buffer );
522 }
523
524
525 /*
526 ===============
527 CreatePitFogImage
528 ===============
529 */
530 void CreatePitFogImage( void ) {
531         byte    data[16][16][4];
532         int             i, j;
533
534         memset( data, 0, sizeof( data ) );
535         for ( i = 0 ; i < 16 ; i++ ) {
536                 int             a;
537
538 #if 0
539                 if ( i > 14 ) {
540                         a = 0;
541                 } else 
542 #endif          
543                 {
544                         a = i * 255 / 15;
545                         if ( a > 255 ) {
546                                 a = 255;
547                         }
548                 }
549
550                 for ( j = 0 ; j < 16 ; j++ ) {
551                         data[j][i][0] =
552                         data[j][i][1] =
553                         data[j][i][2] = 255;
554                         data[j][i][3] = a;
555                 }
556         }
557
558         R_WriteTGA( "shapes/pitFalloff.tga", data[0][0], 16, 16 );
559 }
560
561 /*
562 ===============
563 CreatealphaSquareImage
564 ===============
565 */
566 void CreatealphaSquareImage( void ) {
567         byte    data[16][16][4];
568         int             i, j;
569
570         for ( i = 0 ; i < 16 ; i++ ) {
571                 int             a;
572
573                 for ( j = 0 ; j < 16 ; j++ ) {
574                         if ( i == 0 || i == 15 || j == 0 || j == 15 ) {
575                                 a = 0;
576                         } else {
577                                 a = 255;
578                         }
579                         data[j][i][0] =
580                         data[j][i][1] =
581                         data[j][i][2] = 255;
582                         data[j][i][3] = a;
583                 }
584         }
585
586         R_WriteTGA( "shapes/alphaSquare.tga", data[0][0], 16, 16 );
587 }
588
589 #define NORMAL_MAP_SIZE         32
590
591 /*** NORMALIZATION CUBE MAP CONSTRUCTION ***/
592
593 /* Given a cube map face index, cube map size, and integer 2D face position,
594  * return the cooresponding normalized vector.
595  */
596 static void getCubeVector(int i, int cubesize, int x, int y, float *vector) {
597   float s, t, sc, tc, mag;
598
599   s = ((float)x + 0.5) / (float)cubesize;
600   t = ((float)y + 0.5) / (float)cubesize;
601   sc = s*2.0 - 1.0;
602   tc = t*2.0 - 1.0;
603
604   switch (i) {
605   case 0:
606     vector[0] = 1.0;
607     vector[1] = -tc;
608     vector[2] = -sc;
609     break;
610   case 1:
611     vector[0] = -1.0;
612     vector[1] = -tc;
613     vector[2] = sc;
614     break;
615   case 2:
616     vector[0] = sc;
617     vector[1] = 1.0;
618     vector[2] = tc;
619     break;
620   case 3:
621     vector[0] = sc;
622     vector[1] = -1.0;
623     vector[2] = -tc;
624     break;
625   case 4:
626     vector[0] = sc;
627     vector[1] = -tc;
628     vector[2] = 1.0;
629     break;
630   case 5:
631     vector[0] = -sc;
632     vector[1] = -tc;
633     vector[2] = -1.0;
634     break;
635   }
636
637   mag = idMath::InvSqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]);
638   vector[0] *= mag;
639   vector[1] *= mag;
640   vector[2] *= mag;
641 }
642
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.
647  */
648 static void makeNormalizeVectorCubeMap( idImage *image ) {
649         float vector[3];
650         int i, x, y;
651         byte    *pixels[6];
652         int             size;
653
654         size = NORMAL_MAP_SIZE;
655
656         pixels[0] = (GLubyte*) Mem_Alloc(size*size*4*6);
657
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;
667                   }
668                 }
669         }
670
671         image->GenerateCubeImage( (const byte **)pixels, size,
672                                                    TF_LINEAR, false, TD_HIGH_QUALITY ); 
673
674         Mem_Free(pixels[0]);
675 }
676
677
678
679
680 /*
681 ================
682 R_CreateNoFalloffImage
683
684 This is a solid white texture that is zero clamped.
685 ================
686 */
687 static void R_CreateNoFalloffImage( idImage *image ) {
688         int             x,y;
689         byte    data[16][FALLOFF_TEXTURE_SIZE][4];
690
691         memset( data, 0, sizeof( data ) );
692         for (x=1 ; x<FALLOFF_TEXTURE_SIZE-1 ; x++) {
693                 for (y=1 ; y<15 ; y++) {
694                         data[y][x][0] = 255;
695                         data[y][x][1] = 255;
696                         data[y][x][2] = 255;
697                         data[y][x][3] = 255;
698                 }
699         }
700         image->GenerateImage( (byte *)data, FALLOFF_TEXTURE_SIZE, 16,
701                 TF_DEFAULT, false, TR_CLAMP_TO_ZERO, TD_HIGH_QUALITY );
702 }
703
704
705 /*
706 ================
707 R_FogImage
708
709 We calculate distance correctly in two planes, but the
710 third will still be projection based
711 ================
712 */
713 const int       FOG_SIZE = 128;
714
715 void R_FogImage( idImage *image ) {
716         int             x,y;
717         byte    data[FOG_SIZE][FOG_SIZE][4];
718         int             b;
719
720 float   step[256];
721 int             i;
722 float   remaining = 1.0;
723 for ( i = 0 ; i < 256 ; i++ ) {
724         step[i] = remaining;
725         remaining *= 0.982f;
726 }
727
728         for (x=0 ; x<FOG_SIZE ; x++) {
729                 for (y=0 ; y<FOG_SIZE ; y++) {
730                         float   d;
731
732                         d = idMath::Sqrt( (x - FOG_SIZE/2) * (x - FOG_SIZE/2) 
733                                 + (y - FOG_SIZE/2) * (y - FOG_SIZE / 2) );
734                         d /= FOG_SIZE/2-1;
735
736                         b = (byte)(d * 255);
737                         if ( b <= 0 ) {
738                                 b = 0;
739                         } else if ( b > 255 ) {
740                                 b = 255;
741                         }
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
745                         }
746                         data[y][x][0] =
747                         data[y][x][1] =
748                         data[y][x][2] = 255;
749                         data[y][x][3] = b;
750                 }
751         }
752
753         image->GenerateImage( (byte *)data, FOG_SIZE, FOG_SIZE, 
754                 TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
755 }
756
757
758 /*
759 ================
760 FogFraction
761
762 Height values below zero are inside the fog volume
763 ================
764 */
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 );
769
770 //      return targetHeight >= 0 ? 0 : 1.0;
771
772         // only ranges that cross the ramp range are special
773         if ( targetHeight > 0 && viewHeight > 0 ) {
774                 return 0.0;
775         }
776         if ( targetHeight < -RAMP_RANGE && viewHeight < -RAMP_RANGE ) {
777                 return 1.0;
778         }
779
780         float   above;
781         if ( targetHeight > 0 ) {
782                 above = targetHeight;
783         } else if ( viewHeight > 0 ) {
784                 above = viewHeight;
785         } else {
786                 above = 0;
787         }
788
789         float   rampTop, rampBottom;
790
791         if ( viewHeight > targetHeight ) {
792                 rampTop = viewHeight;
793                 rampBottom = targetHeight;
794         } else {
795                 rampTop = targetHeight;
796                 rampBottom = viewHeight;
797         }
798         if ( rampTop > 0 ) {
799                 rampTop = 0;
800         }
801         if ( rampBottom < -RAMP_RANGE ) {
802                 rampBottom = -RAMP_RANGE;
803         }
804
805         float   rampSlope = 1.0 / RAMP_RANGE;
806
807         if ( !total ) {
808                 return -viewHeight * rampSlope;
809         }
810
811         float ramp = ( 1.0 - ( rampTop * rampSlope + rampBottom * rampSlope ) * -0.5 ) * ( rampTop - rampBottom );
812
813         float   frac = ( total - above - ramp ) / total;
814
815         // after it gets moderately deep, always use full value
816         float deepest = viewHeight < targetHeight ? viewHeight : targetHeight;
817
818         float   deepFrac = deepest / DEEP_RANGE;
819         if ( deepFrac >= 1.0 ) {
820                 return 1.0;
821         }
822
823         frac = frac * ( 1.0 - deepFrac ) + deepFrac;
824
825         return frac;
826 }
827
828 /*
829 ================
830 R_FogEnterImage
831
832 Modulate the fog alpha density based on the distance of the
833 start and end points to the terminator plane
834 ================
835 */
836 void R_FogEnterImage( idImage *image ) {
837         int             x,y;
838         byte    data[FOG_ENTER_SIZE][FOG_ENTER_SIZE][4];
839         int             b;
840
841         for (x=0 ; x<FOG_ENTER_SIZE ; x++) {
842                 for (y=0 ; y<FOG_ENTER_SIZE ; y++) {
843                         float   d;
844
845                         d = FogFraction( x - (FOG_ENTER_SIZE / 2), y - (FOG_ENTER_SIZE / 2) );
846
847                         b = (byte)(d * 255);
848                         if ( b <= 0 ) {
849                                 b = 0;
850                         } else if ( b > 255 ) {
851                                 b = 255;
852                         }
853                         data[y][x][0] =
854                         data[y][x][1] =
855                         data[y][x][2] = 255;
856                         data[y][x][3] = b;
857                 }
858         }
859
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 );
863 }
864
865
866 /*
867 ================
868 R_QuadraticImage
869
870 ================
871 */
872 static const int        QUADRATIC_WIDTH = 32;
873 static const int        QUADRATIC_HEIGHT = 4;
874
875 void R_QuadraticImage( idImage *image ) {
876         int             x,y;
877         byte    data[QUADRATIC_HEIGHT][QUADRATIC_WIDTH][4];
878         int             b;
879
880
881         for (x=0 ; x<QUADRATIC_WIDTH ; x++) {
882                 for (y=0 ; y<QUADRATIC_HEIGHT ; y++) {
883                         float   d;
884
885                         d = x - (QUADRATIC_WIDTH/2 - 0.5);
886                         d = idMath::Fabs( d );
887                         d -= 0.5;
888                         d /= QUADRATIC_WIDTH/2;
889                 
890                         d = 1.0 - d;
891                         d = d * d;
892
893                         b = (byte)(d * 255);
894                         if ( b <= 0 ) {
895                                 b = 0;
896                         } else if ( b > 255 ) {
897                                 b = 255;
898                         }
899                         data[y][x][0] =
900                         data[y][x][1] =
901                         data[y][x][2] = b;
902                         data[y][x][3] = 255;
903                 }
904         }
905
906         image->GenerateImage( (byte *)data, QUADRATIC_WIDTH, QUADRATIC_HEIGHT, 
907                 TF_DEFAULT, false, TR_CLAMP, TD_HIGH_QUALITY );
908 }
909
910 //=====================================================================
911
912
913 typedef struct {
914         char *name;
915         int     minimize, maximize;
916 } filterName_t;
917
918
919
920 /*
921 ===============
922 ChangeTextureFilter
923
924 This resets filtering on all loaded images
925 New images will automatically pick up the current values.
926 ===============
927 */
928 void idImageManager::ChangeTextureFilter( void ) {
929         int             i;
930         idImage *glt;
931         const char      *string;
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}
939 };
940
941         // if these are changed dynamically, it will force another ChangeTextureFilter
942         image_filter.ClearModified();
943         image_anisotropy.ClearModified();
944         image_lodbias.ClearModified();
945
946         string = image_filter.GetString();
947         for ( i = 0; i < 6; i++ ) {
948                 if ( !idStr::Icmp( textureFilters[i].name, string ) ) {
949                         break;
950                 }
951         }
952
953         if ( i == 6 ) {
954                 common->Warning( "bad r_textureFilter: '%s'", string);
955                 // default to LINEAR_MIPMAP_NEAREST
956                 i = 0;
957         }
958
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;
967         }
968         textureLODBias = image_lodbias.GetFloat();
969
970         // change all the existing mipmap texture objects with default filtering
971
972         for ( i = 0 ; i < images.Num() ; i++ ) {
973                 unsigned int    texEnum = GL_TEXTURE_2D;
974
975                 glt = images[ i ];
976
977                 switch( glt->type ) {
978                 case TT_2D:
979                         texEnum = GL_TEXTURE_2D;
980                         break;
981                 case TT_3D:
982                         texEnum = GL_TEXTURE_3D;
983                         break;
984                 case TT_CUBIC:
985                         texEnum = GL_TEXTURE_CUBE_MAP_EXT;
986                         break;
987                 }
988
989                 // make sure we don't start a background load
990                 if ( glt->texnum == idImage::TEXTURE_NOT_LOADED ) {
991                         continue;
992                 }
993                 glt->Bind();
994                 if ( glt->filter == TF_DEFAULT ) {
995                         qglTexParameterf(texEnum, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
996                         qglTexParameterf(texEnum, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
997                 }
998                 if ( glConfig.anisotropicAvailable ) {
999                         qglTexParameterf(texEnum, GL_TEXTURE_MAX_ANISOTROPY_EXT, globalImages->textureAnisotropy );
1000                 }       
1001                 if ( glConfig.textureLODBiasAvailable ) {
1002                         qglTexParameterf(texEnum, GL_TEXTURE_LOD_BIAS_EXT, globalImages->textureLODBias );
1003                 }
1004         }
1005 }
1006
1007 /*
1008 ===============
1009 idImage::Reload
1010 ===============
1011 */
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 );
1017                 return;
1018         }
1019
1020         // check file times
1021         if ( !force ) {
1022                 ID_TIME_T       current;
1023
1024                 if ( cubeFiles != CF_2D ) {
1025                         R_LoadCubeImages( imgName, cubeFiles, NULL, NULL, &current );
1026                 } else {
1027                         // get the current values
1028                         R_LoadImageProgram( imgName, NULL, NULL, NULL, &current );
1029                 }
1030                 if ( current <= timestamp ) {
1031                         return;
1032                 }
1033         }
1034
1035         common->DPrintf( "reloading %s.\n", imgName.c_str() );
1036
1037         PurgeImage();
1038
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 );
1043 }
1044
1045 /*
1046 ===============
1047 R_ReloadImages_f
1048
1049 Regenerate all images that came directly from files that have changed, so
1050 any saved changes will show up in place.
1051
1052 New r_texturesize/r_texturedepth variables will take effect on reload
1053
1054 reloadImages <all>
1055 ===============
1056 */
1057 void R_ReloadImages_f( const idCmdArgs &args ) {
1058         int             i;
1059         idImage *image;
1060         bool    all;
1061         bool    checkPrecompressed;
1062
1063         // this probably isn't necessary...
1064         globalImages->ChangeTextureFilter();
1065
1066         all = false;
1067         checkPrecompressed = false;             // if we are doing this as a vid_restart, look for precompressed like normal
1068
1069         if ( args.Argc() == 2 ) {
1070                 if ( !idStr::Icmp( args.Argv(1), "all" ) ) {
1071                         all = true;
1072                 } else if ( !idStr::Icmp( args.Argv(1), "reload" ) ) {
1073                         all = true;
1074                         checkPrecompressed = true;
1075                 } else {
1076                         common->Printf( "USAGE: reloadImages <all>\n" );
1077                         return;
1078                 }
1079         }
1080
1081         for ( i = 0 ; i < globalImages->images.Num() ; i++ ) {
1082                 image = globalImages->images[ i ];
1083                 image->Reload( checkPrecompressed, all );
1084         }
1085 }
1086
1087 typedef struct {
1088         idImage *image;
1089         int             size;
1090 } sortedImage_t;
1091
1092 /*
1093 =======================
1094 R_QsortImageSizes
1095
1096 =======================
1097 */
1098 static int R_QsortImageSizes( const void *a, const void *b ) {
1099         const sortedImage_t     *ea, *eb;
1100
1101         ea = (sortedImage_t *)a;
1102         eb = (sortedImage_t *)b;
1103
1104         if ( ea->size > eb->size ) {
1105                 return -1;
1106         }
1107         if ( ea->size < eb->size ) {
1108                 return 1;
1109         }
1110         return idStr::Icmp( ea->image->imgName, eb->image->imgName );
1111 }
1112
1113 /*
1114 ===============
1115 R_ListImages_f
1116 ===============
1117 */
1118 void R_ListImages_f( const idCmdArgs &args ) {
1119         int             i, j, partialSize;
1120         idImage *image;
1121         int             totalSize;
1122         int             count = 0;
1123         int             matchTag = 0;
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;
1135
1136         if ( args.Argc() == 1 ) {
1137
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 ) {
1142                         sorted = true;
1143                 } else if ( idStr::Icmp( args.Argv( 1 ), "partial" ) == 0 ) {
1144                         partial = true;
1145                 } else if ( idStr::Icmp( args.Argv( 1 ), "unloaded" ) == 0 ) {
1146                         unloaded = true;
1147                 } else if ( idStr::Icmp( args.Argv( 1 ), "cached" ) == 0 ) {
1148                         cached = true;
1149                 } else if ( idStr::Icmp( args.Argv( 1 ), "uncached" ) == 0 ) {
1150                         uncached = true;
1151                 } else if ( idStr::Icmp( args.Argv( 1 ), "tagged" ) == 0 ) {
1152                         matchTag = 1;
1153                 } else if ( idStr::Icmp( args.Argv( 1 ), "duplicated" ) == 0 ) {
1154                         duplicated = true;
1155                 } else if ( idStr::Icmp( args.Argv( 1 ), "touched" ) == 0 ) {
1156                         touched = true;
1157                 } else if ( idStr::Icmp( args.Argv( 1 ), "classify" ) == 0 ) {
1158                         byClassification = true;
1159                         sorted = true;
1160                 } else if ( idStr::Icmp( args.Argv( 1 ), "oversized" ) == 0 ) {
1161                         byClassification = true;
1162                         sorted = true;
1163                         overSized = true;
1164                 } else {
1165                         failed = true;
1166                 }
1167         } else {
1168                 failed = true;
1169         }
1170
1171         if ( failed ) {
1172                 common->Printf( "usage: listImages [ sorted | partial | unloaded | cached | uncached | tagged | duplicated | touched | classify | showOverSized ]\n" );
1173                 return;
1174         }
1175
1176         const char *header = "       -w-- -h-- filt -fmt-- wrap  size --name-------\n";
1177         common->Printf( "\n%s", header );
1178
1179         totalSize = 0;
1180
1181         sortedImage_t   *sortedArray = (sortedImage_t *)alloca( sizeof( sortedImage_t ) * globalImages->images.Num() );
1182
1183         for ( i = 0 ; i < globalImages->images.Num() ; i++ ) {
1184                 image = globalImages->images[ i ];
1185
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 ) {
1189                                 continue;
1190                         }
1191                 }
1192
1193                 if ( matchTag && image->classification != matchTag ) {
1194                         continue;
1195                 }
1196                 if ( unloaded && image->texnum != idImage::TEXTURE_NOT_LOADED ) {
1197                         continue;
1198                 }
1199                 if ( partial && !image->isPartialImage ) {
1200                         continue;
1201                 }
1202                 if ( cached && ( !image->partialImage || image->texnum == idImage::TEXTURE_NOT_LOADED ) ) {
1203                         continue;
1204                 }
1205                 if ( uncached && ( !image->partialImage || image->texnum != idImage::TEXTURE_NOT_LOADED ) ) {
1206                         continue;
1207                 }
1208
1209                 // only print duplicates (from mismatched wrap / clamp, etc)
1210                 if ( duplicated ) {
1211                         int j;
1212                         for ( j = i+1 ; j < globalImages->images.Num() ; j++ ) {
1213                                 if ( idStr::Icmp( image->imgName, globalImages->images[ j ]->imgName ) == 0 ) {
1214                                         break;
1215                                 }
1216                         }
1217                         if ( j == globalImages->images.Num() ) {
1218                                 continue;
1219                         }
1220                 }
1221
1222                 // "listimages touched" will list only images bound since the last "listimages touched" call
1223                 if ( touched ) {
1224                         if ( image->bindCount == 0 ) {
1225                                 continue;
1226                         }
1227                         image->bindCount = 0;
1228                 }
1229
1230                 if ( sorted ) {
1231                         sortedArray[count].image = image;
1232                         sortedArray[count].size = image->StorageSize();
1233                 } else {
1234                         common->Printf( "%4i:", i );
1235                         image->Print();
1236                 }
1237                 totalSize += image->StorageSize();
1238                 count++;
1239         }
1240
1241         if ( sorted ) {
1242                 qsort( sortedArray, count, sizeof( sortedImage_t ), R_QsortImageSizes );
1243                 partialSize = 0;
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) );
1251                         }
1252                 }
1253         }
1254
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) );
1258
1259         if ( byClassification ) {
1260
1261                 idList< int > classifications[IC_COUNT];
1262
1263                 for ( i = 0 ; i < count ; i++ ) {
1264                         int cl = ClassifyImage( sortedArray[i].image->imgName );
1265                         classifications[ cl ].Append( i );
1266                 }
1267
1268                 for ( i = 0; i < IC_COUNT; i++ ) {
1269                         partialSize = 0;
1270                         idList< int > overSizedList;
1271                         for ( j = 0; j < classifications[ i ].Num(); j++ ) {
1272                                 partialSize += sortedArray[ classifications[ i ][ j ] ].image->StorageSize();
1273                                 if ( overSized ) {
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 ] );
1276                                         }
1277                                 }
1278                         }
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" );
1286                                 }
1287                         }
1288                 }
1289         }
1290
1291 }
1292
1293 /*
1294 ==================
1295 SetNormalPalette
1296
1297 Create a 256 color palette to be used by compressed normal maps
1298 ==================
1299 */
1300 void idImageManager::SetNormalPalette( void ) {
1301         int             i, j;
1302         idVec3  v;
1303         float   t;
1304         //byte temptable[768];
1305         byte    *temptable = compressedPalette;
1306         int             compressedToOriginal[16];
1307
1308         // make an ad-hoc separable compression mapping scheme
1309         for ( i = 0 ; i < 8 ; i++ ) {
1310                 float   f, y;
1311
1312                 f = ( i + 1 ) / 8.5;
1313                 y = idMath::Sqrt( 1.0 - f * f );
1314                 y = 1.0 - y;
1315
1316                 compressedToOriginal[7-i] = 127 - (int)( y * 127 + 0.5 );
1317                 compressedToOriginal[8+i] = 128 + (int)( y * 127 + 0.5 );
1318         }
1319
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;
1325                 } else {
1326                         for ( j = 0 ; j < 14 ; j++ ) {
1327                                 if ( i <= compressedToOriginal[j+1] ) {
1328                                         break;
1329                                 }
1330                         }
1331                         if ( i - compressedToOriginal[j] < compressedToOriginal[j+1] - i ) {
1332                                 originalToCompressed[i] = j;
1333                         } else {
1334                                 originalToCompressed[i] = j + 1;
1335                         }
1336                 }
1337         }
1338
1339 #if 0
1340         for ( i = 0; i < 16; i++ ) {
1341                 for ( j = 0 ; j < 16 ; j++ ) {
1342
1343                         v[0] = ( i - 7.5 ) / 8;
1344                         v[1] = ( j - 7.5 ) / 8;
1345
1346                         t = 1.0 - ( v[0]*v[0] + v[1]*v[1] );
1347                         if ( t < 0 ) {
1348                                 t = 0;
1349                         }
1350                         v[2] = idMath::Sqrt( t );
1351
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] );
1355                 }
1356         }
1357 #else
1358         for ( i = 0; i < 16; i++ ) {
1359                 for ( j = 0 ; j < 16 ; j++ ) {
1360
1361                         v[0] = ( compressedToOriginal[i] - 127.5 ) / 128;
1362                         v[1] = ( compressedToOriginal[j] - 127.5 ) / 128;
1363
1364                         t = 1.0 - ( v[0]*v[0] + v[1]*v[1] );
1365                         if ( t < 0 ) {
1366                                 t = 0;
1367                         }
1368                         v[2] = idMath::Sqrt( t );
1369
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] ));
1373                 }
1374         }
1375 #endif
1376
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;
1381
1382         if ( !glConfig.sharedTexturePaletteAvailable ) {
1383                 return;
1384         }
1385
1386         qglColorTableEXT( GL_SHARED_TEXTURE_PALETTE_EXT,
1387                                            GL_RGB,
1388                                            256,
1389                                            GL_RGB,
1390                                            GL_UNSIGNED_BYTE,
1391                                            temptable );
1392
1393         qglEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
1394 }
1395
1396 /*
1397 ==============
1398 AllocImage
1399
1400 Allocates an idImage, adds it to the list,
1401 copies the name, and adds it to the hash chain.
1402 ==============
1403 */
1404 idImage *idImageManager::AllocImage( const char *name ) {
1405         idImage *image;
1406         int             hash;
1407
1408         if (strlen(name) >= MAX_IMAGE_NAME ) {
1409                 common->Error ("idImageManager::AllocImage: \"%s\" is too long\n", name);
1410         }
1411
1412         hash = idStr( name ).FileNameHash();
1413
1414         image = new idImage;
1415         images.Append( image );
1416
1417         image->hashNext = imageHashTable[hash];
1418         imageHashTable[hash] = image;
1419
1420         image->imgName = name;
1421
1422         return image;
1423 }
1424
1425 /*
1426 ==================
1427 ImageFromFunction
1428
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.
1432 ==================
1433 */
1434 idImage *idImageManager::ImageFromFunction( const char *_name, void (*generatorFunction)( idImage *image ) ) {
1435         idStr name;
1436         idImage *image;
1437         int     hash;
1438
1439         if ( !name ) {
1440                 common->FatalError( "idImageManager::ImageFromFunction: NULL name" );
1441         }
1442
1443         // strip any .tga file extensions from anywhere in the _name
1444         name = _name;
1445         name.Replace( ".tga", "" );
1446         name.BackSlashesToSlashes();
1447
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() );
1454                         }
1455                         return image;
1456                 }
1457         }
1458
1459         // create the image and issue the callback
1460         image = AllocImage( name );
1461
1462         image->generatorFunction = generatorFunction;
1463
1464         if ( image_preload.GetBool() ) {
1465                 // check for precompressed, load is from the front end
1466                 image->referencedOutsideLevelLoad = true;
1467                 image->ActuallyLoadImage( true, false );
1468         }
1469
1470         return image;
1471 }
1472
1473 /*
1474 ===============
1475 ImageFromFile
1476
1477 Finds or loads the given image, always returning a valid image pointer.
1478 Loading of the image may be deferred for dynamic loading.
1479 ==============
1480 */
1481 idImage *idImageManager::ImageFromFile( const char *_name, textureFilter_t filter, bool allowDownSize,
1482                                                  textureRepeat_t repeat, textureDepth_t depth, cubeFiles_t cubeMap ) {
1483         idStr name;
1484         idImage *image;
1485         int hash;
1486
1487         if ( !_name || !_name[0] || idStr::Icmp( _name, "default" ) == 0 || idStr::Icmp( _name, "_default" ) == 0 ) {
1488                 declManager->MediaPrint( "DEFAULTED\n" );
1489                 return globalImages->defaultImage;
1490         }
1491
1492         // strip any .tga file extensions from anywhere in the _name, including image program parameters
1493         name = _name;
1494         name.Replace( ".tga", "" );
1495         name.BackSlashesToSlashes();
1496
1497         //
1498         // see if the image is already loaded, unless we
1499         // are in a reloadImages call
1500         //
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] == '_' ) {
1506                                 return image;
1507                         }
1508                         if ( image->cubeFiles != cubeMap ) {
1509                                 common->Error( "Image '%s' has been referenced with conflicting cube map states", _name );
1510                         }
1511
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
1515                                 continue;
1516                         }
1517
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;
1523                                 }
1524                                 return image;
1525                         }
1526
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;
1531                         }
1532                         if ( image->depth > depth ) {
1533                                 depth = image->depth;
1534                         }
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;
1540                                 }
1541                                 return image;
1542                         }
1543
1544                         image->allowDownSize = allowDownSize;
1545                         image->depth = depth;
1546                         image->levelLoadReferenced = true;
1547                         if ( image->partialImage != NULL ) {
1548                                 image->partialImage->levelLoadReferenced = true;
1549                         }
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() );
1554                         }
1555                         return image;
1556                 }
1557         }
1558
1559         //
1560         // create a new image
1561         //
1562         image = AllocImage( name );
1563
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;
1568         }
1569
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;
1576         
1577         image->levelLoadReferenced = true;
1578
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;
1583
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;
1590
1591                 image->partialImage->levelLoadReferenced = true;
1592
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;
1598
1599                 // let the background file loader know that we can load
1600                 image->precompressedFile = true;
1601
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() );
1605                 } else {
1606                         declManager->MediaPrint( "%s\n", image->imgName.c_str() );
1607                 }
1608                 return image;
1609         }
1610
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() );
1616         } else {
1617                 declManager->MediaPrint( "%s\n", image->imgName.c_str() );
1618         }
1619
1620         return image;
1621 }
1622
1623 /*
1624 ===============
1625 idImageManager::GetImage
1626 ===============
1627 */
1628 idImage *idImageManager::GetImage( const char *_name ) const {
1629         idStr name;
1630         idImage *image;
1631         int hash;
1632
1633         if ( !_name || !_name[0] || idStr::Icmp( _name, "default" ) == 0 || idStr::Icmp( _name, "_default" ) == 0 ) {
1634                 declManager->MediaPrint( "DEFAULTED\n" );
1635                 return globalImages->defaultImage;
1636         }
1637
1638         // strip any .tga file extensions from anywhere in the _name, including image program parameters
1639         name = _name;
1640         name.Replace( ".tga", "" );
1641         name.BackSlashesToSlashes();
1642
1643         //
1644         // look in loaded images
1645         //
1646         hash = name.FileNameHash();
1647         for ( image = imageHashTable[hash]; image; image = image->hashNext ) {
1648                 if ( name.Icmp( image->imgName ) == 0 ) {
1649                         return image;
1650                 }
1651         }
1652
1653         return NULL;
1654 }
1655
1656 /*
1657 ===============
1658 PurgeAllImages
1659 ===============
1660 */
1661 void idImageManager::PurgeAllImages() {
1662         int             i;
1663         idImage *image;
1664
1665         for ( i = 0; i < images.Num() ; i++ ) {
1666                 image = images[i];
1667                 image->PurgeImage();
1668         }
1669 }
1670
1671 /*
1672 ===============
1673 ReloadAllImages
1674 ===============
1675 */
1676 void idImageManager::ReloadAllImages() {
1677         idCmdArgs args;
1678
1679         // build the compressed normal map palette
1680         SetNormalPalette();
1681
1682         args.TokenizeString( "reloadImages reload", false );
1683         R_ReloadImages_f( args );
1684 }
1685
1686 /*
1687 ===============
1688 R_CombineCubeImages_f
1689
1690 Used to combine animations of six separate tga files into
1691 a serials of 6x taller tga files, for preparation to roq compress
1692 ===============
1693 */
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" );
1699                 return;
1700         }
1701
1702         idStr   baseName = args.Argv( 1 );
1703         common->SetRefreshOnPrint( true );
1704
1705         for ( int frameNum = 1 ; frameNum < 10000 ; frameNum++ ) {
1706                 char    filename[MAX_IMAGE_NAME];
1707                 byte    *pics[6];
1708                 int             width, height;
1709                 int             side;
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 );
1713
1714                         common->Printf( "reading %s\n", filename );
1715                         R_LoadImage( filename, &pics[side], &width, &height, NULL, true );
1716
1717                         if ( !pics[side] ) {
1718                                 common->Printf( "not found.\n" );
1719                                 break;
1720                         }
1721
1722                         // convert from "camera" images to native cube map images
1723                         switch( side ) {
1724                         case 0: // forward
1725                                 R_RotatePic( pics[side], width);
1726                                 break;
1727                         case 1: // back
1728                                 R_RotatePic( pics[side], width);
1729                                 R_HorizontalFlip( pics[side], width, height );
1730                                 R_VerticalFlip( pics[side], width, height );
1731                                 break;
1732                         case 2: // left
1733                                 R_VerticalFlip( pics[side], width, height );
1734                                 break;
1735                         case 3: // right
1736                                 R_HorizontalFlip( pics[side], width, height );
1737                                 break;
1738                         case 4: // up
1739                                 R_RotatePic( pics[side], width);
1740                                 break;
1741                         case 5: // down
1742                                 R_RotatePic( pics[side], width);
1743                                 break;
1744                         }
1745                 }
1746
1747                 if ( side != 6 ) {
1748                         for ( int i = 0 ; i < side ; side++ ) {
1749                                 Mem_Free( pics[side] );
1750                         }
1751                         break;
1752                 }
1753
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] );
1758                 }
1759                 sprintf( filename, "%sCM%04i.tga", baseName.c_str(), frameNum );
1760
1761                 common->Printf( "writing %s\n", filename );
1762                 R_WriteTGA( filename, combined, width, height*6 );
1763
1764                 Mem_Free( combined );
1765         }
1766         common->SetRefreshOnPrint( false );
1767 }
1768
1769
1770 /*
1771 ==================
1772 idImage::StartBackgroundImageLoad
1773 ==================
1774 */
1775 void idImage::StartBackgroundImageLoad() {
1776         if ( imageManager.numActiveBackgroundImageLoads >= idImageManager::MAX_BACKGROUND_IMAGE_LOADS ) {
1777                 return;
1778         }
1779         if ( globalImages->image_showBackgroundLoads.GetBool() ) {
1780                 common->Printf( "idImage::StartBackgroundImageLoad: %s\n", imgName.c_str() );
1781         }
1782         backgroundLoadInProgress = true;
1783
1784         if ( !precompressedFile ) {
1785                 common->Warning( "idImageManager::StartBackgroundImageLoad: %s wasn't a precompressed file", imgName.c_str() );
1786                 return;
1787         }
1788
1789         bglNext = globalImages->backgroundImageLoads;
1790         globalImages->backgroundImageLoads = this;
1791
1792         char    filename[MAX_IMAGE_NAME];
1793         ImageProgramStringToCompressedFileName( imgName, filename );
1794
1795         bgl.completed = false;
1796         bgl.f = fileSystem->OpenFileRead( filename );
1797         if ( !bgl.f ) {
1798                 common->Warning( "idImageManager::StartBackgroundImageLoad: Couldn't load %s", imgName.c_str() );
1799                 return;
1800         }
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() );
1805                 return;
1806         }
1807
1808         bgl.file.buffer = R_StaticAlloc( bgl.file.length );
1809
1810         fileSystem->BackgroundDownload( &bgl );
1811
1812         imageManager.numActiveBackgroundImageLoads++;
1813
1814         // purge some images if necessary
1815         int             totalSize = 0;
1816         for ( idImage *check = globalImages->cacheLRU.cacheUsageNext ; check != &globalImages->cacheLRU ; check = check->cacheUsageNext ) {
1817                 totalSize += check->StorageSize();
1818         }
1819         int     needed = this->StorageSize();
1820
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() );
1828                         }
1829                         check->PurgeImage();
1830                 }
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;
1836         }
1837 }
1838
1839 /*
1840 ==================
1841 R_CompleteBackgroundImageLoads
1842
1843 Do we need to worry about vid_restarts here?
1844 ==================
1845 */
1846 void idImageManager::CompleteBackgroundImageLoads() {
1847         idImage *remainingList = NULL;
1848         idImage *next;
1849
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 );
1855                         // upload the image
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() );
1860                         }
1861                 } else {
1862                         image->bglNext = remainingList;
1863                         remainingList = image;
1864                 }
1865         }
1866         if ( image_showBackgroundLoads.GetBool() ) {
1867                 static int prev;
1868                 if ( numActiveBackgroundImageLoads != prev ) {
1869                         prev = numActiveBackgroundImageLoads;
1870                         common->Printf( "background Loads: %i\n", numActiveBackgroundImageLoads );
1871                 }
1872         }
1873
1874         backgroundImageLoads = remainingList;
1875 }
1876
1877 /*
1878 ===============
1879 CheckCvars
1880 ===============
1881 */
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();
1889         }
1890 }
1891
1892 /*
1893 ===============
1894 SumOfUsedImages
1895 ===============
1896 */
1897 int idImageManager::SumOfUsedImages() {
1898         int     total;
1899         int i;
1900         idImage *image;
1901
1902         total = 0;
1903         for ( i = 0; i < images.Num(); i++ ) {
1904                 image = images[i];
1905                 if ( image->frameUsed == backEnd.frameCount ) {
1906                         total += image->StorageSize();
1907                 }
1908         }
1909
1910         return total;
1911 }
1912
1913 /*
1914 ===============
1915 BindNull
1916 ===============
1917 */
1918 void idImageManager::BindNull() {
1919         tmu_t                   *tmu;
1920
1921         tmu = &backEnd.glState.tmu[backEnd.glState.currenttmu];
1922
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 );
1930         }
1931         tmu->textureType = TT_DISABLED;
1932 }
1933
1934 /*
1935 ===============
1936 Init
1937 ===============
1938 */
1939 void idImageManager::Init() {
1940
1941         memset(imageHashTable, 0, sizeof(imageHashTable));
1942
1943         images.Resize( 1024, 1024 );
1944
1945         // clear the cached LRU
1946         cacheLRU.cacheUsageNext = &cacheLRU;
1947         cacheLRU.cacheUsagePrev = &cacheLRU;
1948
1949         // set default texture filter modes
1950         ChangeTextureFilter();
1951
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 );
1969
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 );
1978
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" );
1982
1983         // should forceLoadImages be here?
1984 }
1985
1986 /*
1987 ===============
1988 Shutdown
1989 ===============
1990 */
1991 void idImageManager::Shutdown() {
1992         images.DeleteContents( true );
1993 }
1994
1995 /*
1996 ====================
1997 BeginLevelLoad
1998
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 ====================
2004 */
2005 void idImageManager::BeginLevelLoad() {
2006         insideLevelLoad = true;
2007
2008         for ( int i = 0 ; i < images.Num() ; i++ ) {
2009                 idImage *image = images[ i ];
2010
2011                 // generator function images are always kept around
2012                 if ( image->generatorFunction ) {
2013                         continue;
2014                 }
2015
2016                 if ( com_purgeAll.GetBool() ) {
2017                         image->PurgeImage();
2018                 }
2019
2020                 image->levelLoadReferenced = false;
2021         }
2022 }
2023
2024 /*
2025 ====================
2026 EndLevelLoad
2027
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.
2031
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 ====================
2037 */
2038 void idImageManager::EndLevelLoad() {
2039         int                     start = Sys_Milliseconds();
2040
2041         insideLevelLoad = false;
2042         if ( idAsyncNetwork::serverDedicated.GetInteger() ) {
2043                 return;
2044         }
2045
2046         common->Printf( "----- idImageManager::EndLevelLoad -----\n" );
2047
2048         int             purgeCount = 0;
2049         int             keepCount = 0;
2050         int             loadCount = 0;
2051
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 ) {
2056                         continue;
2057                 }
2058
2059                 if ( !image->levelLoadReferenced && !image->referencedOutsideLevelLoad ) {
2060 //                      common->Printf( "Purging %s\n", image->imgName.c_str() );
2061                         purgeCount++;
2062                         image->PurgeImage();
2063                 } else if ( image->texnum != idImage::TEXTURE_NOT_LOADED ) {
2064 //                      common->Printf( "Keeping %s\n", image->imgName.c_str() );
2065                         keepCount++;
2066                 }
2067         }
2068
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 ) {
2073                         continue;
2074                 }
2075
2076                 if ( image->levelLoadReferenced && image->texnum == idImage::TEXTURE_NOT_LOADED && !image->partialImage ) {
2077 //                      common->Printf( "Loading %s\n", image->imgName.c_str() );
2078                         loadCount++;
2079                         image->ActuallyLoadImage( true, false );
2080
2081                         if ( ( loadCount & 15 ) == 0 ) {
2082                                 session->PacifierUpdate();
2083                         }
2084                 }
2085         }
2086
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" );
2093 }
2094
2095 /*
2096 ===============
2097 idImageManager::StartBuild
2098 ===============
2099 */
2100 void idImageManager::StartBuild() {
2101         ddsList.Clear();
2102         ddsHash.Free();
2103 }
2104
2105 /*
2106 ===============
2107 idImageManager::FinishBuild
2108 ===============
2109 */
2110 void idImageManager::FinishBuild( bool removeDups ) {
2111         idFile *batchFile;
2112         if ( removeDups ) {
2113                 ddsList.Clear();
2114                 char *buffer = NULL;
2115                 fileSystem->ReadFile( "makedds.bat", (void**)&buffer );
2116                 if ( buffer ) {
2117                         idStr str = buffer;
2118                         while ( str.Length() ) {
2119                                 int n = str.Find( '\n' );
2120                                 if ( n > 0 ) {
2121                                         idStr line = str.Left( n + 1 );
2122                                         idStr right;
2123                                         str.Right( str.Length() - n - 1, right );
2124                                         str = right;
2125                                         ddsList.AddUnique( line );
2126                                 } else {
2127                                         break;
2128                                 }
2129                         }
2130                 }
2131         }
2132         batchFile = fileSystem->OpenFileWrite( ( removeDups ) ? "makedds2.bat" : "makedds.bat" );
2133         if ( batchFile ) {
2134                 int i;
2135                 int ddsNum = ddsList.Num();
2136
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 );
2140                 }
2141                 fileSystem->CloseFile( batchFile );
2142         }
2143         ddsList.Clear();
2144         ddsHash.Free();
2145 }
2146
2147 /*
2148 ===============
2149 idImageManager::AddDDSCommand
2150 ===============
2151 */
2152 void idImageManager::AddDDSCommand( const char *cmd ) {
2153         int i, key;
2154
2155         if ( !( cmd && *cmd ) ) {
2156                 return;
2157         }
2158
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 ) {
2162                         break;
2163                 }
2164         }
2165
2166         if ( i == -1 ) {
2167                 ddsList.Append( cmd );
2168         }
2169 }
2170
2171 /*
2172 ===============
2173 idImageManager::PrintMemInfo
2174 ===============
2175 */
2176 void idImageManager::PrintMemInfo( MemInfo_t *mi ) {
2177         int i, j, total = 0;
2178         int *sortIndex;
2179         idFile *f;
2180
2181         f = fileSystem->OpenFileWrite( mi->filebase + "_images.txt" );
2182         if ( !f ) {
2183                 return;
2184         }
2185
2186         // sort first
2187         sortIndex = new int[images.Num()];
2188
2189         for ( i = 0; i < images.Num(); i++ ) {
2190                 sortIndex[i] = i;
2191         }
2192
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;
2199                         }
2200                 }
2201         }
2202
2203         // print next
2204         for ( i = 0; i < images.Num(); i++ ) {
2205                 idImage *im = images[sortIndex[i]];
2206                 int size;
2207
2208                 size = im->StorageSize();
2209                 total += size;
2210
2211                 f->Printf( "%s %3i %s\n", idStr::FormatNumber( size ).c_str(), im->refCount, im->imgName.c_str() );
2212         }
2213
2214         delete sortIndex;
2215         mi->imageAssetsTotal = total;
2216
2217         f->Printf( "\nTotal image bytes allocated: %s\n", idStr::FormatNumber( total ).c_str() );
2218         fileSystem->CloseFile( f );
2219 }