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 "sys/platform.h"
30 #include "framework/async/AsyncNetwork.h"
31 #include "framework/Session.h"
32 #include "renderer/tr_local.h"
33 
34 #include "renderer/Image.h"
35 
36 #include "framework/GameCallbacks_local.h"
37 
38 const char *imageFilter[] = {
39 	"GL_LINEAR_MIPMAP_NEAREST",
40 	"GL_LINEAR_MIPMAP_LINEAR",
41 	"GL_NEAREST",
42 	"GL_LINEAR",
43 	"GL_NEAREST_MIPMAP_NEAREST",
44 	"GL_NEAREST_MIPMAP_LINEAR",
45 	NULL
46 };
47 
48 idCVar idImageManager::image_filter( "image_filter", imageFilter[1], CVAR_RENDERER | CVAR_ARCHIVE, "changes texture filtering on mipmapped images", imageFilter, idCmdSystem::ArgCompletion_String<imageFilter> );
49 idCVar idImageManager::image_anisotropy( "image_anisotropy", "1", CVAR_RENDERER | CVAR_ARCHIVE, "set the maximum texture anisotropy if available" );
50 idCVar idImageManager::image_lodbias( "image_lodbias", "0", CVAR_RENDERER | CVAR_ARCHIVE, "change lod bias on mipmapped images" );
51 idCVar idImageManager::image_downSize( "image_downSize", "0", CVAR_RENDERER | CVAR_ARCHIVE, "controls texture downsampling" );
52 idCVar idImageManager::image_forceDownSize( "image_forceDownSize", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "" );
53 idCVar idImageManager::image_roundDown( "image_roundDown", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "round bad sizes down to nearest power of two" );
54 idCVar idImageManager::image_colorMipLevels( "image_colorMipLevels", "0", CVAR_RENDERER | CVAR_BOOL, "development aid to see texture mip usage" );
55 idCVar idImageManager::image_preload( "image_preload", "1", CVAR_RENDERER | CVAR_BOOL | CVAR_ARCHIVE, "if 0, dynamically load all images" );
56 idCVar idImageManager::image_useCompression( "image_useCompression", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "0 = force everything to high quality" );
57 idCVar idImageManager::image_useAllFormats( "image_useAllFormats", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "allow alpha/intensity/luminance/luminance+alpha" );
58 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" );
59 idCVar idImageManager::image_usePrecompressedTextures( "image_usePrecompressedTextures", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "use .dds files if present" );
60 idCVar idImageManager::image_writePrecompressedTextures( "image_writePrecompressedTextures", "0", CVAR_RENDERER | CVAR_BOOL, "write .dds files if necessary" );
61 idCVar idImageManager::image_writeNormalTGA( "image_writeNormalTGA", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the final normal maps for debugging" );
62 idCVar idImageManager::image_writeNormalTGAPalletized( "image_writeNormalTGAPalletized", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the final palletized normal maps for debugging" );
63 idCVar idImageManager::image_writeTGA( "image_writeTGA", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the non normal maps for debugging" );
64 idCVar idImageManager::image_useOffLineCompression( "image_useOfflineCompression", "0", CVAR_RENDERER | CVAR_BOOL, "write a batch file for offline compression of DDS files" );
65 idCVar idImageManager::image_cacheMinK( "image_cacheMinK", "200", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "maximum KB of precompressed files to read at specification time" );
66 idCVar idImageManager::image_cacheMegs( "image_cacheMegs", "20", CVAR_RENDERER | CVAR_ARCHIVE, "maximum MB set aside for temporary loading of full-sized precompressed images" );
67 idCVar idImageManager::image_useCache( "image_useCache", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "1 = do background load image caching" );
68 idCVar idImageManager::image_showBackgroundLoads( "image_showBackgroundLoads", "0", CVAR_RENDERER | CVAR_BOOL, "1 = print number of outstanding background loads" );
69 idCVar idImageManager::image_downSizeSpecular( "image_downSizeSpecular", "0", CVAR_RENDERER | CVAR_ARCHIVE, "controls specular downsampling" );
70 idCVar idImageManager::image_downSizeBump( "image_downSizeBump", "0", CVAR_RENDERER | CVAR_ARCHIVE, "controls normal map downsampling" );
71 idCVar idImageManager::image_downSizeSpecularLimit( "image_downSizeSpecularLimit", "64", CVAR_RENDERER | CVAR_ARCHIVE, "controls specular downsampled limit" );
72 idCVar idImageManager::image_downSizeBumpLimit( "image_downSizeBumpLimit", "128", CVAR_RENDERER | CVAR_ARCHIVE, "controls normal map downsample limit" );
73 idCVar idImageManager::image_ignoreHighQuality( "image_ignoreHighQuality", "0", CVAR_RENDERER | CVAR_ARCHIVE, "ignore high quality setting on materials" );
74 idCVar idImageManager::image_downSizeLimit( "image_downSizeLimit", "256", CVAR_RENDERER | CVAR_ARCHIVE, "controls diffuse map downsample limit" );
75 // do this with a pointer, in case we want to make the actual manager
76 // a private virtual subclass
77 idImageManager	imageManager;
78 idImageManager	*globalImages = &imageManager;
79 
80 
81 enum IMAGE_CLASSIFICATION {
82 	IC_NPC,
83 	IC_WEAPON,
84 	IC_MONSTER,
85 	IC_MODELGEOMETRY,
86 	IC_ITEMS,
87 	IC_MODELSOTHER,
88 	IC_GUIS,
89 	IC_WORLDGEOMETRY,
90 	IC_OTHER,
91 	IC_COUNT
92 };
93 
94 struct imageClassificate_t {
95 	const char *rootPath;
96 	const char *desc;
97 	int type;
98 	int maxWidth;
99 	int maxHeight;
100 };
101 
102 typedef idList< int > intList;
103 
104 const imageClassificate_t IC_Info[] = {
105 	{ "models/characters", "Characters", IC_NPC, 512, 512 },
106 	{ "models/weapons", "Weapons", IC_WEAPON, 512, 512 },
107 	{ "models/monsters", "Monsters", IC_MONSTER, 512, 512 },
108 	{ "models/mapobjects", "Model Geometry", IC_MODELGEOMETRY, 512, 512 },
109 	{ "models/items", "Items", IC_ITEMS, 512, 512 },
110 	{ "models", "Other model textures", IC_MODELSOTHER, 512, 512 },
111 	{ "guis/assets", "Guis", IC_GUIS, 256, 256 },
112 	{ "textures", "World Geometry", IC_WORLDGEOMETRY, 256, 256 },
113 	{ "", "Other", IC_OTHER, 256, 256 }
114 };
115 
116 
117 
ClassifyImage(const char * name)118 static int ClassifyImage( const char *name ) {
119 	idStr str;
120 	str = name;
121 	for ( int i = 0; i < IC_COUNT; i++ ) {
122 		if ( str.Find( IC_Info[i].rootPath, false ) == 0 ) {
123 			return IC_Info[i].type;
124 		}
125 	}
126 	return IC_OTHER;
127 }
128 
129 /*
130 ================
131 R_RampImage
132 
133 Creates a 0-255 ramp image
134 ================
135 */
R_RampImage(idImage * image)136 static void R_RampImage( idImage *image ) {
137 	int		x;
138 	byte	data[256][4];
139 
140 	for (x=0 ; x<256 ; x++) {
141 		data[x][0] =
142 		data[x][1] =
143 		data[x][2] =
144 		data[x][3] = x;
145 	}
146 
147 	image->GenerateImage( (byte *)data, 256, 1,
148 		TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
149 }
150 
151 /*
152 ================
153 R_SpecularTableImage
154 
155 Creates a ramp that matches our fudged specular calculation
156 ================
157 */
R_SpecularTableImage(idImage * image)158 static void R_SpecularTableImage( idImage *image ) {
159 	int		x;
160 	byte	data[256][4];
161 
162 	for (x=0 ; x<256 ; x++) {
163 		float f = x/255.f;
164 #if 0
165 		f = pow(f, 16);
166 #else
167 		// this is the behavior of the hacked up fragment programs that
168 		// can't really do a power function
169 		f = (f-0.75)*4;
170 		if ( f < 0 ) {
171 			f = 0;
172 		}
173 		f = f * f;
174 #endif
175 		int		b = (int)(f * 255);
176 
177 		data[x][0] =
178 		data[x][1] =
179 		data[x][2] =
180 		data[x][3] = b;
181 	}
182 
183 	image->GenerateImage( (byte *)data, 256, 1,
184 		TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
185 }
186 
187 
188 /*
189 ================
190 R_Specular2DTableImage
191 
192 Create a 2D table that calculates ( reflection dot , specularity )
193 ================
194 */
R_Specular2DTableImage(idImage * image)195 static void R_Specular2DTableImage( idImage *image ) {
196 	int		x, y;
197 	byte	data[256][256][4];
198 
199 	memset( data, 0, sizeof( data ) );
200 		for ( x = 0 ; x < 256 ; x++ ) {
201 			float f = x / 255.0f;
202 		for ( y = 0; y < 256; y++ ) {
203 
204 			int b = (int)( pow( f, y ) * 255.0f );
205 			if ( b == 0 ) {
206 				// as soon as b equals zero all remaining values in this column are going to be zero
207 				// we early out to avoid pow() underflows
208 				break;
209 			}
210 
211 			data[y][x][0] =
212 			data[y][x][1] =
213 			data[y][x][2] =
214 			data[y][x][3] = b;
215 		}
216 	}
217 
218 	image->GenerateImage( (byte *)data, 256, 256, TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
219 }
220 
221 
222 
223 /*
224 ================
225 R_AlphaRampImage
226 
227 Creates a 0-255 ramp image
228 ================
229 */
230 #if 0
231 static void R_AlphaRampImage( idImage *image ) {
232 	int		x;
233 	byte	data[256][4];
234 
235 	for (x=0 ; x<256 ; x++) {
236 		data[x][0] =
237 		data[x][1] =
238 		data[x][2] = 255;
239 		data[x][3] = x;
240 	}
241 
242 	image->GenerateImage( (byte *)data, 256, 1,
243 		TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
244 }
245 #endif
246 
247 
248 
249 /*
250 ==================
251 R_CreateDefaultImage
252 
253 the default image will be grey with a white box outline
254 to allow you to see the mapping coordinates on a surface
255 ==================
256 */
257 #define	DEFAULT_SIZE	16
MakeDefault()258 void idImage::MakeDefault() {
259 	int		x, y;
260 	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
261 
262 	if ( com_developer.GetBool() ) {
263 		// grey center
264 		for ( y = 0 ; y < DEFAULT_SIZE ; y++ ) {
265 			for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
266 				data[y][x][0] = 32;
267 				data[y][x][1] = 32;
268 				data[y][x][2] = 32;
269 				data[y][x][3] = 255;
270 			}
271 		}
272 
273 		// white border
274 		for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
275 			data[0][x][0] =
276 				data[0][x][1] =
277 				data[0][x][2] =
278 				data[0][x][3] = 255;
279 
280 			data[x][0][0] =
281 				data[x][0][1] =
282 				data[x][0][2] =
283 				data[x][0][3] = 255;
284 
285 			data[DEFAULT_SIZE-1][x][0] =
286 				data[DEFAULT_SIZE-1][x][1] =
287 				data[DEFAULT_SIZE-1][x][2] =
288 				data[DEFAULT_SIZE-1][x][3] = 255;
289 
290 			data[x][DEFAULT_SIZE-1][0] =
291 				data[x][DEFAULT_SIZE-1][1] =
292 				data[x][DEFAULT_SIZE-1][2] =
293 				data[x][DEFAULT_SIZE-1][3] = 255;
294 		}
295 	} else {
296 		for ( y = 0 ; y < DEFAULT_SIZE ; y++ ) {
297 			for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
298 				data[y][x][0] = 0;
299 				data[y][x][1] = 0;
300 				data[y][x][2] = 0;
301 				data[y][x][3] = 0;
302 			}
303 		}
304 	}
305 
306 	GenerateImage( (byte *)data,
307 		DEFAULT_SIZE, DEFAULT_SIZE,
308 		TF_DEFAULT, true, TR_REPEAT, TD_DEFAULT );
309 
310 	defaulted = true;
311 }
312 
R_DefaultImage(idImage * image)313 static void R_DefaultImage( idImage *image ) {
314 	image->MakeDefault();
315 }
316 
R_WhiteImage(idImage * image)317 static void R_WhiteImage( idImage *image ) {
318 	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
319 
320 	// solid white texture
321 	memset( data, 255, sizeof( data ) );
322 	image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
323 		TF_DEFAULT, false, TR_REPEAT, TD_DEFAULT );
324 }
325 
R_BlackImage(idImage * image)326 static void R_BlackImage( idImage *image ) {
327 	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
328 
329 	// solid black texture
330 	memset( data, 0, sizeof( data ) );
331 	image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
332 		TF_DEFAULT, false, TR_REPEAT, TD_DEFAULT );
333 }
334 
335 
336 // the size determines how far away from the edge the blocks start fading
337 static const int BORDER_CLAMP_SIZE = 32;
R_BorderClampImage(idImage * image)338 static void R_BorderClampImage( idImage *image ) {
339 	byte	data[BORDER_CLAMP_SIZE][BORDER_CLAMP_SIZE][4];
340 
341 	// solid white texture with a single pixel black border
342 	memset( data, 255, sizeof( data ) );
343 	for ( int i = 0 ; i < BORDER_CLAMP_SIZE ; i++ ) {
344 		data[i][0][0] =
345 		data[i][0][1] =
346 		data[i][0][2] =
347 		data[i][0][3] =
348 
349 		data[i][BORDER_CLAMP_SIZE-1][0] =
350 		data[i][BORDER_CLAMP_SIZE-1][1] =
351 		data[i][BORDER_CLAMP_SIZE-1][2] =
352 		data[i][BORDER_CLAMP_SIZE-1][3] =
353 
354 		data[0][i][0] =
355 		data[0][i][1] =
356 		data[0][i][2] =
357 		data[0][i][3] =
358 
359 		data[BORDER_CLAMP_SIZE-1][i][0] =
360 		data[BORDER_CLAMP_SIZE-1][i][1] =
361 		data[BORDER_CLAMP_SIZE-1][i][2] =
362 		data[BORDER_CLAMP_SIZE-1][i][3] = 0;
363 	}
364 
365 	image->GenerateImage( (byte *)data, BORDER_CLAMP_SIZE, BORDER_CLAMP_SIZE,
366 		TF_LINEAR /* TF_NEAREST */, false, TR_CLAMP_TO_BORDER, TD_DEFAULT );
367 
368 	if ( !glConfig.isInitialized ) {
369 		// can't call qglTexParameterfv yet
370 		return;
371 	}
372 	// explicit zero border
373 	float	color[4];
374 	color[0] = color[1] = color[2] = color[3] = 0;
375 	qglTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color );
376 }
377 
R_RGBA8Image(idImage * image)378 static void R_RGBA8Image( idImage *image ) {
379 	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
380 
381 	memset( data, 0, sizeof( data ) );
382 	data[0][0][0] = 16;
383 	data[0][0][1] = 32;
384 	data[0][0][2] = 48;
385 	data[0][0][3] = 96;
386 
387 	image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
388 		TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
389 }
390 
391 #if 0
392 static void R_RGB8Image( idImage *image ) {
393 	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
394 
395 	memset( data, 0, sizeof( data ) );
396 	data[0][0][0] = 16;
397 	data[0][0][1] = 32;
398 	data[0][0][2] = 48;
399 	data[0][0][3] = 255;
400 
401 	image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
402 		TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
403 }
404 #endif
405 
R_AlphaNotchImage(idImage * image)406 static void R_AlphaNotchImage( idImage *image ) {
407 	byte	data[2][4];
408 
409 	// this is used for alpha test clip planes
410 
411 	data[0][0] = data[0][1] = data[0][2] = 255;
412 	data[0][3] = 0;
413 	data[1][0] = data[1][1] = data[1][2] = 255;
414 	data[1][3] = 255;
415 
416 	image->GenerateImage( (byte *)data, 2, 1,
417 		TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
418 }
419 
R_FlatNormalImage(idImage * image)420 static void R_FlatNormalImage( idImage *image ) {
421 	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
422 	int		i;
423 
424 	int red = ( globalImages->image_useNormalCompression.GetInteger() == 1 ) ? 0 : 3;
425 	int alpha = ( red == 0 ) ? 3 : 0;
426 	// flat normal map for default bunp mapping
427 	for ( i = 0 ; i < 4 ; i++ ) {
428 		data[0][i][red] = 128;
429 		data[0][i][1] = 128;
430 		data[0][i][2] = 255;
431 		data[0][i][alpha] = 255;
432 	}
433 	image->GenerateImage( (byte *)data, 2, 2,
434 		TF_DEFAULT, true, TR_REPEAT, TD_HIGH_QUALITY );
435 }
436 
R_AmbientNormalImage(idImage * image)437 static void R_AmbientNormalImage( idImage *image ) {
438 	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
439 	int		i;
440 
441 	int red = ( globalImages->image_useNormalCompression.GetInteger() == 1 ) ? 0 : 3;
442 	int alpha = ( red == 0 ) ? 3 : 0;
443 	// flat normal map for default bunp mapping
444 	for ( i = 0 ; i < 4 ; i++ ) {
445 		data[0][i][red] = (byte)(255 * tr.ambientLightVector[0]);
446 		data[0][i][1] = (byte)(255 * tr.ambientLightVector[1]);
447 		data[0][i][2] = (byte)(255 * tr.ambientLightVector[2]);
448 		data[0][i][alpha] = 255;
449 	}
450 	const byte	*pics[6];
451 	for ( i = 0 ; i < 6 ; i++ ) {
452 		pics[i] = data[0][0];
453 	}
454 	// this must be a cube map for fragment programs to simply substitute for the normalization cube map
455 	image->GenerateCubeImage( pics, 2, TF_DEFAULT, true, TD_HIGH_QUALITY );
456 }
457 
458 
459 #if 0
460 static void CreateSquareLight( void ) {
461 	byte		*buffer;
462 	int			x, y;
463 	int			dx, dy;
464 	int			d;
465 	int			width, height;
466 
467 	width = height = 128;
468 
469 	buffer = (byte *)R_StaticAlloc( 128 * 128 * 4 );
470 
471 	for ( x = 0 ; x < 128 ; x++ ) {
472 		if ( x < 32 ) {
473 			dx = 32 - x;
474 		} else if ( x > 96 ) {
475 			dx = x - 96;
476 		} else {
477 			dx = 0;
478 		}
479 		for ( y = 0 ; y < 128 ; y++ ) {
480 			if ( y < 32 ) {
481 				dy = 32 - y;
482 			} else if ( y > 96 ) {
483 				dy = y - 96;
484 			} else {
485 				dy = 0;
486 			}
487 			d = (byte)idMath::Sqrt( dx * dx + dy * dy );
488 			if ( d > 32 ) {
489 				d = 32;
490 			}
491 			d = 255 - d * 8;
492 			if ( d < 0 ) {
493 				d = 0;
494 			}
495 			buffer[(y*128+x)*4+0] =
496 			buffer[(y*128+x)*4+1] =
497 			buffer[(y*128+x)*4+2] = d;
498 			buffer[(y*128+x)*4+3] = 255;
499 		}
500 	}
501 
502 	R_WriteTGA( "lights/squarelight.tga", buffer, width, height );
503 
504 	R_StaticFree( buffer );
505 }
506 
507 static void CreateFlashOff( void ) {
508 	byte		*buffer;
509 	int			x, y;
510 	int			d;
511 	int			width, height;
512 
513 	width = 256;
514 	height = 4;
515 
516 	buffer = (byte *)R_StaticAlloc( width * height * 4 );
517 
518 	for ( x = 0 ; x < width ; x++ ) {
519 		for ( y = 0 ; y < height ; y++ ) {
520 			d = 255 - ( x * 256 / width );
521 			buffer[(y*width+x)*4+0] =
522 			buffer[(y*width+x)*4+1] =
523 			buffer[(y*width+x)*4+2] = d;
524 			buffer[(y*width+x)*4+3] = 255;
525 		}
526 	}
527 
528 	R_WriteTGA( "lights/flashoff.tga", buffer, width, height );
529 
530 	R_StaticFree( buffer );
531 }
532 #endif
533 
534 
535 /*
536 ===============
537 CreatePitFogImage
538 ===============
539 */
CreatePitFogImage(void)540 void CreatePitFogImage( void ) {
541 	byte	data[16][16][4];
542 	int		i, j;
543 
544 	memset( data, 0, sizeof( data ) );
545 	for ( i = 0 ; i < 16 ; i++ ) {
546 		int		a;
547 
548 #if 0
549 		if ( i > 14 ) {
550 			a = 0;
551 		} else
552 #endif
553 		{
554 			a = i * 255 / 15;
555 			if ( a > 255 ) {
556 				a = 255;
557 			}
558 		}
559 
560 		for ( j = 0 ; j < 16 ; j++ ) {
561 			data[j][i][0] =
562 			data[j][i][1] =
563 			data[j][i][2] = 255;
564 			data[j][i][3] = a;
565 		}
566 	}
567 
568 	R_WriteTGA( "shapes/pitFalloff.tga", data[0][0], 16, 16 );
569 }
570 
571 /*
572 ===============
573 CreatealphaSquareImage
574 ===============
575 */
CreatealphaSquareImage(void)576 void CreatealphaSquareImage( void ) {
577 	byte	data[16][16][4];
578 	int		i, j;
579 
580 	for ( i = 0 ; i < 16 ; i++ ) {
581 		int		a;
582 
583 		for ( j = 0 ; j < 16 ; j++ ) {
584 			if ( i == 0 || i == 15 || j == 0 || j == 15 ) {
585 				a = 0;
586 			} else {
587 				a = 255;
588 			}
589 			data[j][i][0] =
590 			data[j][i][1] =
591 			data[j][i][2] = 255;
592 			data[j][i][3] = a;
593 		}
594 	}
595 
596 	R_WriteTGA( "shapes/alphaSquare.tga", data[0][0], 16, 16 );
597 }
598 
599 #define	NORMAL_MAP_SIZE		32
600 
601 /*** NORMALIZATION CUBE MAP CONSTRUCTION ***/
602 
603 /* Given a cube map face index, cube map size, and integer 2D face position,
604  * return the cooresponding normalized vector.
605  */
getCubeVector(int i,int cubesize,int x,int y,float * vector)606 static void getCubeVector(int i, int cubesize, int x, int y, float *vector) {
607   float s, t, sc, tc, mag;
608 
609   s = ((float)x + 0.5) / (float)cubesize;
610   t = ((float)y + 0.5) / (float)cubesize;
611   sc = s*2.0 - 1.0;
612   tc = t*2.0 - 1.0;
613 
614   switch (i) {
615   case 0:
616 	vector[0] = 1.0;
617 	vector[1] = -tc;
618 	vector[2] = -sc;
619 	break;
620   case 1:
621 	vector[0] = -1.0;
622 	vector[1] = -tc;
623 	vector[2] = sc;
624 	break;
625   case 2:
626 	vector[0] = sc;
627 	vector[1] = 1.0;
628 	vector[2] = tc;
629 	break;
630   case 3:
631 	vector[0] = sc;
632 	vector[1] = -1.0;
633 	vector[2] = -tc;
634 	break;
635   case 4:
636 	vector[0] = sc;
637 	vector[1] = -tc;
638 	vector[2] = 1.0;
639 	break;
640   case 5:
641 	vector[0] = -sc;
642 	vector[1] = -tc;
643 	vector[2] = -1.0;
644 	break;
645   default:
646 	common->Error ("getCubeVector: invalid cube map face index");
647 	return;
648   }
649 
650   mag = idMath::InvSqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]);
651   vector[0] *= mag;
652   vector[1] *= mag;
653   vector[2] *= mag;
654 }
655 
656 /* Initialize a cube map texture object that generates RGB values
657  * that when expanded to a [-1,1] range in the register combiners
658  * form a normalized vector matching the per-pixel vector used to
659  * access the cube map.
660  */
makeNormalizeVectorCubeMap(idImage * image)661 static void makeNormalizeVectorCubeMap( idImage *image ) {
662 	float vector[3] = { };
663 	int i, x, y;
664 	byte	*pixels[6];
665 	int		size;
666 
667 	size = NORMAL_MAP_SIZE;
668 
669 	pixels[0] = (GLubyte*) Mem_Alloc(size*size*4*6);
670 
671 	for (i = 0; i < 6; i++) {
672 		pixels[i] = pixels[0] + i*size*size*4;
673 		for (y = 0; y < size; y++) {
674 		  for (x = 0; x < size; x++) {
675 			getCubeVector(i, size, x, y, vector);
676 			pixels[i][4*(y*size+x) + 0] = (byte)(128 + 127*vector[0]);
677 			pixels[i][4*(y*size+x) + 1] = (byte)(128 + 127*vector[1]);
678 			pixels[i][4*(y*size+x) + 2] = (byte)(128 + 127*vector[2]);
679 			pixels[i][4*(y*size+x) + 3] = 255;
680 		  }
681 		}
682 	}
683 
684 	image->GenerateCubeImage( (const byte **)pixels, size,
685 						   TF_LINEAR, false, TD_HIGH_QUALITY );
686 
687 	Mem_Free(pixels[0]);
688 }
689 
690 
691 
692 
693 /*
694 ================
695 R_CreateNoFalloffImage
696 
697 This is a solid white texture that is zero clamped.
698 ================
699 */
R_CreateNoFalloffImage(idImage * image)700 static void R_CreateNoFalloffImage( idImage *image ) {
701 	int		x,y;
702 	byte	data[16][FALLOFF_TEXTURE_SIZE][4];
703 
704 	memset( data, 0, sizeof( data ) );
705 	for (x=1 ; x<FALLOFF_TEXTURE_SIZE-1 ; x++) {
706 		for (y=1 ; y<15 ; y++) {
707 			data[y][x][0] = 255;
708 			data[y][x][1] = 255;
709 			data[y][x][2] = 255;
710 			data[y][x][3] = 255;
711 		}
712 	}
713 	image->GenerateImage( (byte *)data, FALLOFF_TEXTURE_SIZE, 16,
714 		TF_DEFAULT, false, TR_CLAMP_TO_ZERO, TD_HIGH_QUALITY );
715 }
716 
717 
718 /*
719 ================
720 R_FogImage
721 
722 We calculate distance correctly in two planes, but the
723 third will still be projection based
724 ================
725 */
726 const int	FOG_SIZE = 128;
727 
R_FogImage(idImage * image)728 void R_FogImage( idImage *image ) {
729 	int		x,y;
730 	byte	data[FOG_SIZE][FOG_SIZE][4];
731 	int		b;
732 
733 float	step[256];
734 int		i;
735 float	remaining = 1.0;
736 for ( i = 0 ; i < 256 ; i++ ) {
737 	step[i] = remaining;
738 	remaining *= 0.982f;
739 }
740 
741 	for (x=0 ; x<FOG_SIZE ; x++) {
742 		for (y=0 ; y<FOG_SIZE ; y++) {
743 			float	d;
744 
745 			d = idMath::Sqrt( (x - FOG_SIZE/2) * (x - FOG_SIZE/2)
746 				+ (y - FOG_SIZE/2) * (y - FOG_SIZE / 2) );
747 			d /= FOG_SIZE/2-1;
748 
749 			b = (byte)(d * 255);
750 			if ( b <= 0 ) {
751 				b = 0;
752 			} else if ( b > 255 ) {
753 				b = 255;
754 			}
755 b = (byte)(255 * ( 1.0 - step[b] ));
756 			if ( x == 0 || x == FOG_SIZE-1 || y == 0 || y == FOG_SIZE-1 ) {
757 				b = 255;		// avoid clamping issues
758 			}
759 			data[y][x][0] =
760 			data[y][x][1] =
761 			data[y][x][2] = 255;
762 			data[y][x][3] = b;
763 		}
764 	}
765 
766 	image->GenerateImage( (byte *)data, FOG_SIZE, FOG_SIZE,
767 		TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
768 }
769 
770 
771 /*
772 ================
773 FogFraction
774 
775 Height values below zero are inside the fog volume
776 ================
777 */
778 static const float	RAMP_RANGE =	8;
779 static const float	DEEP_RANGE =	-30;
FogFraction(float viewHeight,float targetHeight)780 static float	FogFraction( float viewHeight, float targetHeight ) {
781 	float	total = idMath::Fabs( targetHeight - viewHeight );
782 
783 //	return targetHeight >= 0 ? 0 : 1.0;
784 
785 	// only ranges that cross the ramp range are special
786 	if ( targetHeight > 0 && viewHeight > 0 ) {
787 		return 0.0;
788 	}
789 	if ( targetHeight < -RAMP_RANGE && viewHeight < -RAMP_RANGE ) {
790 		return 1.0;
791 	}
792 
793 	float	above;
794 	if ( targetHeight > 0 ) {
795 		above = targetHeight;
796 	} else if ( viewHeight > 0 ) {
797 		above = viewHeight;
798 	} else {
799 		above = 0;
800 	}
801 
802 	float	rampTop, rampBottom;
803 
804 	if ( viewHeight > targetHeight ) {
805 		rampTop = viewHeight;
806 		rampBottom = targetHeight;
807 	} else {
808 		rampTop = targetHeight;
809 		rampBottom = viewHeight;
810 	}
811 	if ( rampTop > 0 ) {
812 		rampTop = 0;
813 	}
814 	if ( rampBottom < -RAMP_RANGE ) {
815 		rampBottom = -RAMP_RANGE;
816 	}
817 
818 	float	rampSlope = 1.0 / RAMP_RANGE;
819 
820 	if ( !total ) {
821 		return -viewHeight * rampSlope;
822 	}
823 
824 	float ramp = ( 1.0 - ( rampTop * rampSlope + rampBottom * rampSlope ) * -0.5 ) * ( rampTop - rampBottom );
825 
826 	float	frac = ( total - above - ramp ) / total;
827 
828 	// after it gets moderately deep, always use full value
829 	float deepest = viewHeight < targetHeight ? viewHeight : targetHeight;
830 
831 	float	deepFrac = deepest / DEEP_RANGE;
832 	if ( deepFrac >= 1.0 ) {
833 		return 1.0;
834 	}
835 
836 	frac = frac * ( 1.0 - deepFrac ) + deepFrac;
837 
838 	return frac;
839 }
840 
841 /*
842 ================
843 R_FogEnterImage
844 
845 Modulate the fog alpha density based on the distance of the
846 start and end points to the terminator plane
847 ================
848 */
R_FogEnterImage(idImage * image)849 void R_FogEnterImage( idImage *image ) {
850 	int		x,y;
851 	byte	data[FOG_ENTER_SIZE][FOG_ENTER_SIZE][4];
852 	int		b;
853 
854 	for (x=0 ; x<FOG_ENTER_SIZE ; x++) {
855 		for (y=0 ; y<FOG_ENTER_SIZE ; y++) {
856 			float	d;
857 
858 			d = FogFraction( x - (FOG_ENTER_SIZE / 2), y - (FOG_ENTER_SIZE / 2) );
859 
860 			b = (byte)(d * 255);
861 			if ( b <= 0 ) {
862 				b = 0;
863 			} else if ( b > 255 ) {
864 				b = 255;
865 			}
866 			data[y][x][0] =
867 			data[y][x][1] =
868 			data[y][x][2] = 255;
869 			data[y][x][3] = b;
870 		}
871 	}
872 
873 	// if mipmapped, acutely viewed surfaces fade wrong
874 	image->GenerateImage( (byte *)data, FOG_ENTER_SIZE, FOG_ENTER_SIZE,
875 		TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
876 }
877 
878 
879 /*
880 ================
881 R_QuadraticImage
882 
883 ================
884 */
885 static const int	QUADRATIC_WIDTH = 32;
886 static const int	QUADRATIC_HEIGHT = 4;
887 
R_QuadraticImage(idImage * image)888 void R_QuadraticImage( idImage *image ) {
889 	int		x,y;
890 	byte	data[QUADRATIC_HEIGHT][QUADRATIC_WIDTH][4];
891 	int		b;
892 
893 
894 	for (x=0 ; x<QUADRATIC_WIDTH ; x++) {
895 		for (y=0 ; y<QUADRATIC_HEIGHT ; y++) {
896 			float	d;
897 
898 			d = x - (QUADRATIC_WIDTH/2 - 0.5);
899 			d = idMath::Fabs( d );
900 			d -= 0.5;
901 			d /= QUADRATIC_WIDTH/2;
902 
903 			d = 1.0 - d;
904 			d = d * d;
905 
906 			b = (byte)(d * 255);
907 			if ( b <= 0 ) {
908 				b = 0;
909 			} else if ( b > 255 ) {
910 				b = 255;
911 			}
912 			data[y][x][0] =
913 			data[y][x][1] =
914 			data[y][x][2] = b;
915 			data[y][x][3] = 255;
916 		}
917 	}
918 
919 	image->GenerateImage( (byte *)data, QUADRATIC_WIDTH, QUADRATIC_HEIGHT,
920 		TF_DEFAULT, false, TR_CLAMP, TD_HIGH_QUALITY );
921 }
922 
923 //=====================================================================
924 
925 
926 typedef struct {
927 	const char *name;
928 	int	minimize, maximize;
929 } filterName_t;
930 
931 
932 
933 /*
934 ===============
935 ChangeTextureFilter
936 
937 This resets filtering on all loaded images
938 New images will automatically pick up the current values.
939 ===============
940 */
ChangeTextureFilter(void)941 void idImageManager::ChangeTextureFilter( void ) {
942 	int		i;
943 	idImage	*glt;
944 	const char	*string;
945 static const filterName_t textureFilters[] = {
946 	{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
947 	{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR},
948 	{"GL_NEAREST", GL_NEAREST, GL_NEAREST},
949 	{"GL_LINEAR", GL_LINEAR, GL_LINEAR},
950 	{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
951 	{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}
952 };
953 
954 	// if these are changed dynamically, it will force another ChangeTextureFilter
955 	image_filter.ClearModified();
956 	image_anisotropy.ClearModified();
957 	image_lodbias.ClearModified();
958 
959 	string = image_filter.GetString();
960 	for ( i = 0; i < 6; i++ ) {
961 		if ( !idStr::Icmp( textureFilters[i].name, string ) ) {
962 			break;
963 		}
964 	}
965 
966 	if ( i == 6 ) {
967 		common->Warning( "bad r_textureFilter: '%s'", string);
968 		// default to LINEAR_MIPMAP_NEAREST
969 		i = 0;
970 	}
971 
972 	// set the values for future images
973 	textureMinFilter = textureFilters[i].minimize;
974 	textureMaxFilter = textureFilters[i].maximize;
975 	textureAnisotropy = image_anisotropy.GetFloat();
976 	if ( textureAnisotropy < 1 ) {
977 		textureAnisotropy = 1;
978 	} else if ( textureAnisotropy > glConfig.maxTextureAnisotropy ) {
979 		textureAnisotropy = glConfig.maxTextureAnisotropy;
980 	}
981 	textureLODBias = image_lodbias.GetFloat();
982 
983 	// change all the existing mipmap texture objects with default filtering
984 
985 	for ( i = 0 ; i < images.Num() ; i++ ) {
986 		unsigned int	texEnum = GL_TEXTURE_2D;
987 
988 		glt = images[ i ];
989 
990 		switch( glt->type ) {
991 		case TT_2D:
992 			texEnum = GL_TEXTURE_2D;
993 			break;
994 		case TT_3D:
995 			texEnum = GL_TEXTURE_3D;
996 			break;
997 		case TT_CUBIC:
998 			texEnum = GL_TEXTURE_CUBE_MAP_EXT;
999 			break;
1000 		}
1001 
1002 		// make sure we don't start a background load
1003 		if ( glt->texnum == idImage::TEXTURE_NOT_LOADED ) {
1004 			continue;
1005 		}
1006 		glt->Bind();
1007 		if ( glt->filter == TF_DEFAULT ) {
1008 			qglTexParameterf(texEnum, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
1009 			qglTexParameterf(texEnum, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
1010 		}
1011 		if ( glConfig.anisotropicAvailable ) {
1012 			qglTexParameterf(texEnum, GL_TEXTURE_MAX_ANISOTROPY_EXT, globalImages->textureAnisotropy );
1013 		}
1014 		if ( glConfig.textureLODBiasAvailable ) {
1015 			qglTexParameterf(texEnum, GL_TEXTURE_LOD_BIAS_EXT, globalImages->textureLODBias );
1016 		}
1017 	}
1018 }
1019 
1020 /*
1021 ===============
1022 idImage::Reload
1023 ===============
1024 */
Reload(bool checkPrecompressed,bool force)1025 void idImage::Reload( bool checkPrecompressed, bool force ) {
1026 	// always regenerate functional images
1027 	if ( generatorFunction ) {
1028 		common->DPrintf( "regenerating %s.\n", imgName.c_str() );
1029 		generatorFunction( this );
1030 		return;
1031 	}
1032 
1033 	// check file times
1034 	if ( !force ) {
1035 		ID_TIME_T	current;
1036 
1037 		if ( cubeFiles != CF_2D ) {
1038 			R_LoadCubeImages( imgName, cubeFiles, NULL, NULL, &current );
1039 		} else {
1040 			// get the current values
1041 			R_LoadImageProgram( imgName, NULL, NULL, NULL, &current );
1042 		}
1043 		if ( current <= timestamp ) {
1044 			return;
1045 		}
1046 	}
1047 
1048 	common->DPrintf( "reloading %s.\n", imgName.c_str() );
1049 
1050 	PurgeImage();
1051 
1052 	// force no precompressed image check, which will cause it to be reloaded
1053 	// from source, and another precompressed file generated.
1054 	// Load is from the front end, so the back end must be synced
1055 	ActuallyLoadImage( checkPrecompressed, false );
1056 }
1057 
1058 /*
1059 ===============
1060 R_ReloadImages_f
1061 
1062 Regenerate all images that came directly from files that have changed, so
1063 any saved changes will show up in place.
1064 
1065 New r_texturesize/r_texturedepth variables will take effect on reload
1066 
1067 reloadImages <all>
1068 ===============
1069 */
R_ReloadImages_f(const idCmdArgs & args)1070 void R_ReloadImages_f( const idCmdArgs &args ) {
1071 	int		i;
1072 	idImage	*image;
1073 	bool	all;
1074 	bool	checkPrecompressed;
1075 
1076 	// DG: notify the game DLL about the reloadImages command
1077 	if(gameCallbacks.reloadImagesCB != NULL)
1078 	{
1079 		gameCallbacks.reloadImagesCB(gameCallbacks.reloadImagesUserArg, args);
1080 	}
1081 
1082 	// this probably isn't necessary...
1083 	globalImages->ChangeTextureFilter();
1084 
1085 	all = false;
1086 	checkPrecompressed = false;		// if we are doing this as a vid_restart, look for precompressed like normal
1087 
1088 	if ( args.Argc() == 2 ) {
1089 		if ( !idStr::Icmp( args.Argv(1), "all" ) ) {
1090 			all = true;
1091 		} else if ( !idStr::Icmp( args.Argv(1), "reload" ) ) {
1092 			all = true;
1093 			checkPrecompressed = true;
1094 		} else {
1095 			common->Printf( "USAGE: reloadImages <all>\n" );
1096 			return;
1097 		}
1098 	}
1099 
1100 	for ( i = 0 ; i < globalImages->images.Num() ; i++ ) {
1101 		image = globalImages->images[ i ];
1102 		image->Reload( checkPrecompressed, all );
1103 	}
1104 }
1105 
1106 typedef struct {
1107 	idImage	*image;
1108 	int		size;
1109 } sortedImage_t;
1110 
1111 /*
1112 =======================
1113 R_QsortImageSizes
1114 
1115 =======================
1116 */
R_QsortImageSizes(const void * a,const void * b)1117 static int R_QsortImageSizes( const void *a, const void *b ) {
1118 	const sortedImage_t	*ea, *eb;
1119 
1120 	ea = (sortedImage_t *)a;
1121 	eb = (sortedImage_t *)b;
1122 
1123 	if ( ea->size > eb->size ) {
1124 		return -1;
1125 	}
1126 	if ( ea->size < eb->size ) {
1127 		return 1;
1128 	}
1129 	return idStr::Icmp( ea->image->imgName, eb->image->imgName );
1130 }
1131 
1132 /*
1133 ===============
1134 R_ListImages_f
1135 ===============
1136 */
R_ListImages_f(const idCmdArgs & args)1137 void R_ListImages_f( const idCmdArgs &args ) {
1138 	int		i, j, partialSize;
1139 	idImage	*image;
1140 	int		totalSize;
1141 	int		count = 0;
1142 	int		matchTag = 0;
1143 	bool	uncompressedOnly = false;
1144 	bool	unloaded = false;
1145 	bool	partial = false;
1146 	bool	cached = false;
1147 	bool	uncached = false;
1148 	bool	failed = false;
1149 	bool	touched = false;
1150 	bool	sorted = false;
1151 	bool	duplicated = false;
1152 	bool	byClassification = false;
1153 	bool	overSized = false;
1154 
1155 	if ( args.Argc() == 1 ) {
1156 
1157 	} else if ( args.Argc() == 2 ) {
1158 		if ( idStr::Icmp( args.Argv( 1 ), "uncompressed" ) == 0 ) {
1159 			uncompressedOnly = true;
1160 		} else if ( idStr::Icmp( args.Argv( 1 ), "sorted" ) == 0 ) {
1161 			sorted = true;
1162 		} else if ( idStr::Icmp( args.Argv( 1 ), "partial" ) == 0 ) {
1163 			partial = true;
1164 		} else if ( idStr::Icmp( args.Argv( 1 ), "unloaded" ) == 0 ) {
1165 			unloaded = true;
1166 		} else if ( idStr::Icmp( args.Argv( 1 ), "cached" ) == 0 ) {
1167 			cached = true;
1168 		} else if ( idStr::Icmp( args.Argv( 1 ), "uncached" ) == 0 ) {
1169 			uncached = true;
1170 		} else if ( idStr::Icmp( args.Argv( 1 ), "tagged" ) == 0 ) {
1171 			matchTag = 1;
1172 		} else if ( idStr::Icmp( args.Argv( 1 ), "duplicated" ) == 0 ) {
1173 			duplicated = true;
1174 		} else if ( idStr::Icmp( args.Argv( 1 ), "touched" ) == 0 ) {
1175 			touched = true;
1176 		} else if ( idStr::Icmp( args.Argv( 1 ), "classify" ) == 0 ) {
1177 			byClassification = true;
1178 			sorted = true;
1179 		} else if ( idStr::Icmp( args.Argv( 1 ), "oversized" ) == 0 ) {
1180 			byClassification = true;
1181 			sorted = true;
1182 			overSized = true;
1183 		} else {
1184 			failed = true;
1185 		}
1186 	} else {
1187 		failed = true;
1188 	}
1189 
1190 	if ( failed ) {
1191 		common->Printf( "usage: listImages [ sorted | partial | unloaded | cached | uncached | tagged | duplicated | touched | classify | showOverSized ]\n" );
1192 		return;
1193 	}
1194 
1195 	const char *header = "       -w-- -h-- filt -fmt-- wrap  size --name-------\n";
1196 	common->Printf( "\n%s", header );
1197 
1198 	totalSize = 0;
1199 
1200 	sortedImage_t	*sortedArray = (sortedImage_t *)_alloca( sizeof( sortedImage_t ) * globalImages->images.Num() );
1201 
1202 	for ( i = 0 ; i < globalImages->images.Num() ; i++ ) {
1203 		image = globalImages->images[ i ];
1204 
1205 		if ( uncompressedOnly ) {
1206 			if ( ( image->internalFormat >= GL_COMPRESSED_RGB_S3TC_DXT1_EXT && image->internalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT )
1207 				|| image->internalFormat == GL_COLOR_INDEX8_EXT ) {
1208 				continue;
1209 			}
1210 		}
1211 
1212 		if ( matchTag && image->classification != matchTag ) {
1213 			continue;
1214 		}
1215 		if ( unloaded && image->texnum != idImage::TEXTURE_NOT_LOADED ) {
1216 			continue;
1217 		}
1218 		if ( partial && !image->isPartialImage ) {
1219 			continue;
1220 		}
1221 		if ( cached && ( !image->partialImage || image->texnum == idImage::TEXTURE_NOT_LOADED ) ) {
1222 			continue;
1223 		}
1224 		if ( uncached && ( !image->partialImage || image->texnum != idImage::TEXTURE_NOT_LOADED ) ) {
1225 			continue;
1226 		}
1227 
1228 		// only print duplicates (from mismatched wrap / clamp, etc)
1229 		if ( duplicated ) {
1230 			int j;
1231 			for ( j = i+1 ; j < globalImages->images.Num() ; j++ ) {
1232 				if ( idStr::Icmp( image->imgName, globalImages->images[ j ]->imgName ) == 0 ) {
1233 					break;
1234 				}
1235 			}
1236 			if ( j == globalImages->images.Num() ) {
1237 				continue;
1238 			}
1239 		}
1240 
1241 		// "listimages touched" will list only images bound since the last "listimages touched" call
1242 		if ( touched ) {
1243 			if ( image->bindCount == 0 ) {
1244 				continue;
1245 			}
1246 			image->bindCount = 0;
1247 		}
1248 
1249 		if ( sorted ) {
1250 			sortedArray[count].image = image;
1251 			sortedArray[count].size = image->StorageSize();
1252 		} else {
1253 			common->Printf( "%4i:",	i );
1254 			image->Print();
1255 		}
1256 		totalSize += image->StorageSize();
1257 		count++;
1258 	}
1259 
1260 	if ( sorted ) {
1261 		qsort( sortedArray, count, sizeof( sortedImage_t ), R_QsortImageSizes );
1262 		partialSize = 0;
1263 		for ( i = 0 ; i < count ; i++ ) {
1264 			common->Printf( "%4i:",	i );
1265 			sortedArray[i].image->Print();
1266 			partialSize += sortedArray[i].image->StorageSize();
1267 			if ( ( (i+1) % 10 ) == 0 ) {
1268 				common->Printf( "-------- %5.1f of %5.1f megs --------\n",
1269 					partialSize / (1024*1024.0), totalSize / (1024*1024.0) );
1270 			}
1271 		}
1272 	}
1273 
1274 	common->Printf( "%s", header );
1275 	common->Printf( " %i images (%i total)\n", count, globalImages->images.Num() );
1276 	common->Printf( " %5.1f total megabytes of images\n\n\n", totalSize / (1024*1024.0) );
1277 
1278 	if ( byClassification ) {
1279 
1280 		idList< int > classifications[IC_COUNT];
1281 
1282 		for ( i = 0 ; i < count ; i++ ) {
1283 			int cl = ClassifyImage( sortedArray[i].image->imgName );
1284 			classifications[ cl ].Append( i );
1285 		}
1286 
1287 		for ( i = 0; i < IC_COUNT; i++ ) {
1288 			partialSize = 0;
1289 			idList< int > overSizedList;
1290 			for ( j = 0; j < classifications[ i ].Num(); j++ ) {
1291 				partialSize += sortedArray[ classifications[ i ][ j ] ].image->StorageSize();
1292 				if ( overSized ) {
1293 					if ( sortedArray[ classifications[ i ][ j ] ].image->uploadWidth > IC_Info[i].maxWidth && sortedArray[ classifications[ i ][ j ] ].image->uploadHeight > IC_Info[i].maxHeight ) {
1294 						overSizedList.Append( classifications[ i ][ j ] );
1295 					}
1296 				}
1297 			}
1298 			common->Printf ( " Classification %s contains %i images using %5.1f megabytes\n", IC_Info[i].desc, classifications[i].Num(), partialSize / ( 1024*1024.0 ) );
1299 			if ( overSized && overSizedList.Num() ) {
1300 				common->Printf( "  The following images may be oversized\n" );
1301 				for ( j = 0; j < overSizedList.Num(); j++ ) {
1302 					common->Printf( "    " );
1303 					sortedArray[ overSizedList[ j ] ].image->Print();
1304 					common->Printf( "\n" );
1305 				}
1306 			}
1307 		}
1308 	}
1309 
1310 }
1311 
1312 /*
1313 ==================
1314 SetNormalPalette
1315 
1316 Create a 256 color palette to be used by compressed normal maps
1317 ==================
1318 */
SetNormalPalette(void)1319 void idImageManager::SetNormalPalette( void ) {
1320 	int		i, j;
1321 	idVec3	v;
1322 	float	t;
1323 	//byte temptable[768];
1324 	byte	*temptable = compressedPalette;
1325 	int		compressedToOriginal[16];
1326 
1327 	// make an ad-hoc separable compression mapping scheme
1328 	for ( i = 0 ; i < 8 ; i++ ) {
1329 		float	f, y;
1330 
1331 		f = ( i + 1 ) / 8.5;
1332 		y = idMath::Sqrt( 1.0 - f * f );
1333 		y = 1.0 - y;
1334 
1335 		compressedToOriginal[7-i] = 127 - (int)( y * 127 + 0.5 );
1336 		compressedToOriginal[8+i] = 128 + (int)( y * 127 + 0.5 );
1337 	}
1338 
1339 	for ( i = 0 ; i < 256 ; i++ ) {
1340 		if ( i <= compressedToOriginal[0] ) {
1341 			originalToCompressed[i] = 0;
1342 		} else if ( i >= compressedToOriginal[15] ) {
1343 			originalToCompressed[i] = 15;
1344 		} else {
1345 			for ( j = 0 ; j < 14 ; j++ ) {
1346 				if ( i <= compressedToOriginal[j+1] ) {
1347 					break;
1348 				}
1349 			}
1350 			if ( i - compressedToOriginal[j] < compressedToOriginal[j+1] - i ) {
1351 				originalToCompressed[i] = j;
1352 			} else {
1353 				originalToCompressed[i] = j + 1;
1354 			}
1355 		}
1356 	}
1357 
1358 #if 0
1359 	for ( i = 0; i < 16; i++ ) {
1360 		for ( j = 0 ; j < 16 ; j++ ) {
1361 
1362 			v[0] = ( i - 7.5 ) / 8;
1363 			v[1] = ( j - 7.5 ) / 8;
1364 
1365 			t = 1.0 - ( v[0]*v[0] + v[1]*v[1] );
1366 			if ( t < 0 ) {
1367 				t = 0;
1368 			}
1369 			v[2] = idMath::Sqrt( t );
1370 
1371 			temptable[(i*16+j)*3+0] = 128 + floor( 127 * v[0] + 0.5 );
1372 			temptable[(i*16+j)*3+1] = 128 + floor( 127 * v[1] );
1373 			temptable[(i*16+j)*3+2] = 128 + floor( 127 * v[2] );
1374 		}
1375 	}
1376 #else
1377 	for ( i = 0; i < 16; i++ ) {
1378 		for ( j = 0 ; j < 16 ; j++ ) {
1379 
1380 			v[0] = ( compressedToOriginal[i] - 127.5 ) / 128;
1381 			v[1] = ( compressedToOriginal[j] - 127.5 ) / 128;
1382 
1383 			t = 1.0 - ( v[0]*v[0] + v[1]*v[1] );
1384 			if ( t < 0 ) {
1385 				t = 0;
1386 			}
1387 			v[2] = idMath::Sqrt( t );
1388 
1389 			temptable[(i*16+j)*3+0] = (byte)(128 + floor( 127 * v[0] + 0.5 ));
1390 			temptable[(i*16+j)*3+1] = (byte)(128 + floor( 127 * v[1] ));
1391 			temptable[(i*16+j)*3+2] = (byte)(128 + floor( 127 * v[2] ));
1392 		}
1393 	}
1394 #endif
1395 
1396 	// color 255 will be the "nullnormal" color for no reflection
1397 	temptable[255*3+0] =
1398 	temptable[255*3+1] =
1399 	temptable[255*3+2] = 128;
1400 
1401 	if ( !glConfig.sharedTexturePaletteAvailable ) {
1402 		return;
1403 	}
1404 
1405 	qglColorTableEXT( GL_SHARED_TEXTURE_PALETTE_EXT,
1406 					   GL_RGB,
1407 					   256,
1408 					   GL_RGB,
1409 					   GL_UNSIGNED_BYTE,
1410 					   temptable );
1411 
1412 	qglEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
1413 }
1414 
1415 /*
1416 ==============
1417 AllocImage
1418 
1419 Allocates an idImage, adds it to the list,
1420 copies the name, and adds it to the hash chain.
1421 ==============
1422 */
AllocImage(const char * name)1423 idImage *idImageManager::AllocImage( const char *name ) {
1424 	idImage *image;
1425 	int		hash;
1426 
1427 	if (strlen(name) >= MAX_IMAGE_NAME ) {
1428 		common->Error ("idImageManager::AllocImage: \"%s\" is too long\n", name);
1429 	}
1430 
1431 	hash = idStr( name ).FileNameHash();
1432 
1433 	image = new idImage;
1434 	images.Append( image );
1435 
1436 	image->hashNext = imageHashTable[hash];
1437 	imageHashTable[hash] = image;
1438 
1439 	image->imgName = name;
1440 
1441 	return image;
1442 }
1443 
1444 /*
1445 ==================
1446 ImageFromFunction
1447 
1448 Images that are procedurally generated are allways specified
1449 with a callback which must work at any time, allowing the OpenGL
1450 system to be completely regenerated if needed.
1451 ==================
1452 */
ImageFromFunction(const char * _name,void (* generatorFunction)(idImage * image))1453 idImage *idImageManager::ImageFromFunction( const char *_name, void (*generatorFunction)( idImage *image ) ) {
1454 	idStr name;
1455 	idImage	*image;
1456 	int	hash;
1457 
1458 	if ( !name ) {
1459 		common->FatalError( "idImageManager::ImageFromFunction: NULL name" );
1460 	}
1461 
1462 	// strip any .tga file extensions from anywhere in the _name
1463 	name = _name;
1464 	name.Replace( ".tga", "" );
1465 	name.BackSlashesToSlashes();
1466 
1467 	// see if the image already exists
1468 	hash = name.FileNameHash();
1469 	for ( image = imageHashTable[hash] ; image; image = image->hashNext ) {
1470 		if ( name.Icmp( image->imgName ) == 0 ) {
1471 			if ( image->generatorFunction != generatorFunction ) {
1472 				common->DPrintf( "WARNING: reused image %s with mixed generators\n", name.c_str() );
1473 			}
1474 			return image;
1475 		}
1476 	}
1477 
1478 	// create the image and issue the callback
1479 	image = AllocImage( name );
1480 
1481 	image->generatorFunction = generatorFunction;
1482 
1483 	if ( image_preload.GetBool() ) {
1484 		// check for precompressed, load is from the front end
1485 		image->referencedOutsideLevelLoad = true;
1486 		image->ActuallyLoadImage( true, false );
1487 	}
1488 
1489 	return image;
1490 }
1491 
1492 /*
1493 ===============
1494 ImageFromFile
1495 
1496 Finds or loads the given image, always returning a valid image pointer.
1497 Loading of the image may be deferred for dynamic loading.
1498 ==============
1499 */
ImageFromFile(const char * _name,textureFilter_t filter,bool allowDownSize,textureRepeat_t repeat,textureDepth_t depth,cubeFiles_t cubeMap)1500 idImage	*idImageManager::ImageFromFile( const char *_name, textureFilter_t filter, bool allowDownSize,
1501 						 textureRepeat_t repeat, textureDepth_t depth, cubeFiles_t cubeMap ) {
1502 	idStr name;
1503 	idImage	*image;
1504 	int hash;
1505 
1506 	if ( !_name || !_name[0] || idStr::Icmp( _name, "default" ) == 0 || idStr::Icmp( _name, "_default" ) == 0 ) {
1507 		declManager->MediaPrint( "DEFAULTED\n" );
1508 		return globalImages->defaultImage;
1509 	}
1510 
1511 	// strip any .tga file extensions from anywhere in the _name, including image program parameters
1512 	name = _name;
1513 	name.Replace( ".tga", "" );
1514 	name.BackSlashesToSlashes();
1515 
1516 	//
1517 	// see if the image is already loaded, unless we
1518 	// are in a reloadImages call
1519 	//
1520 	hash = name.FileNameHash();
1521 	for ( image = imageHashTable[hash]; image; image = image->hashNext ) {
1522 		if ( name.Icmp( image->imgName ) == 0 ) {
1523 			// the built in's, like _white and _flat always match the other options
1524 			if ( name[0] == '_' ) {
1525 				return image;
1526 			}
1527 			if ( image->cubeFiles != cubeMap ) {
1528 				common->Error( "Image '%s' has been referenced with conflicting cube map states", _name );
1529 			}
1530 
1531 			if ( image->filter != filter || image->repeat != repeat ) {
1532 				// we might want to have the system reset these parameters on every bind and
1533 				// share the image data
1534 				continue;
1535 			}
1536 
1537 			if ( image->allowDownSize == allowDownSize && image->depth == depth ) {
1538 				// note that it is used this level load
1539 				image->levelLoadReferenced = true;
1540 				if ( image->partialImage != NULL ) {
1541 					image->partialImage->levelLoadReferenced = true;
1542 				}
1543 				return image;
1544 			}
1545 
1546 			// the same image is being requested, but with a different allowDownSize or depth
1547 			// so pick the highest of the two and reload the old image with those parameters
1548 			if ( !image->allowDownSize ) {
1549 				allowDownSize = false;
1550 			}
1551 			if ( image->depth > depth ) {
1552 				depth = image->depth;
1553 			}
1554 			if ( image->allowDownSize == allowDownSize && image->depth == depth ) {
1555 				// the already created one is already the highest quality
1556 				image->levelLoadReferenced = true;
1557 				if ( image->partialImage != NULL ) {
1558 					image->partialImage->levelLoadReferenced = true;
1559 				}
1560 				return image;
1561 			}
1562 
1563 			image->allowDownSize = allowDownSize;
1564 			image->depth = depth;
1565 			image->levelLoadReferenced = true;
1566 			if ( image->partialImage != NULL ) {
1567 				image->partialImage->levelLoadReferenced = true;
1568 			}
1569 			if ( image_preload.GetBool() && !insideLevelLoad ) {
1570 				image->referencedOutsideLevelLoad = true;
1571 				image->ActuallyLoadImage( true, false );	// check for precompressed, load is from front end
1572 				declManager->MediaPrint( "%ix%i %s (reload for mixed references)\n", image->uploadWidth, image->uploadHeight, image->imgName.c_str() );
1573 			}
1574 			return image;
1575 		}
1576 	}
1577 
1578 	//
1579 	// create a new image
1580 	//
1581 	image = AllocImage( name );
1582 
1583 	// HACK: to allow keep fonts from being mip'd, as new ones will be introduced with localization
1584 	// this keeps us from having to make a material for each font tga
1585 	if ( name.Find( "fontImage_") >= 0 ) {
1586 		allowDownSize = false;
1587 	}
1588 
1589 	image->allowDownSize = allowDownSize;
1590 	image->repeat = repeat;
1591 	image->depth = depth;
1592 	image->type = TT_2D;
1593 	image->cubeFiles = cubeMap;
1594 	image->filter = filter;
1595 
1596 	image->levelLoadReferenced = true;
1597 
1598 	// also create a shrunken version if we are going to dynamically cache the full size image
1599 	if ( image->ShouldImageBePartialCached() ) {
1600 		// if we only loaded part of the file, create a new idImage for the shrunken version
1601 		image->partialImage = new idImage;
1602 
1603 		image->partialImage->allowDownSize = allowDownSize;
1604 		image->partialImage->repeat = repeat;
1605 		image->partialImage->depth = depth;
1606 		image->partialImage->type = TT_2D;
1607 		image->partialImage->cubeFiles = cubeMap;
1608 		image->partialImage->filter = filter;
1609 
1610 		image->partialImage->levelLoadReferenced = true;
1611 
1612 		// we don't bother hooking this into the hash table for lookup, but we do add it to the manager
1613 		// list for listImages
1614 		globalImages->images.Append( image->partialImage );
1615 		image->partialImage->imgName = image->imgName;
1616 		image->partialImage->isPartialImage = true;
1617 
1618 		// let the background file loader know that we can load
1619 		image->precompressedFile = true;
1620 
1621 		if ( image_preload.GetBool() && !insideLevelLoad ) {
1622 			image->partialImage->ActuallyLoadImage( true, false );	// check for precompressed, load is from front end
1623 			declManager->MediaPrint( "%ix%i %s\n", image->partialImage->uploadWidth, image->partialImage->uploadHeight, image->imgName.c_str() );
1624 		} else {
1625 			declManager->MediaPrint( "%s\n", image->imgName.c_str() );
1626 		}
1627 		return image;
1628 	}
1629 
1630 	// load it if we aren't in a level preload
1631 	if ( image_preload.GetBool() && !insideLevelLoad ) {
1632 		image->referencedOutsideLevelLoad = true;
1633 		image->ActuallyLoadImage( true, false );	// check for precompressed, load is from front end
1634 		declManager->MediaPrint( "%ix%i %s\n", image->uploadWidth, image->uploadHeight, image->imgName.c_str() );
1635 	} else {
1636 		declManager->MediaPrint( "%s\n", image->imgName.c_str() );
1637 	}
1638 
1639 	return image;
1640 }
1641 
1642 /*
1643 ===============
1644 idImageManager::GetImage
1645 ===============
1646 */
GetImage(const char * _name) const1647 idImage *idImageManager::GetImage( const char *_name ) const {
1648 	idStr name;
1649 	idImage	*image;
1650 	int hash;
1651 
1652 	if ( !_name || !_name[0] || idStr::Icmp( _name, "default" ) == 0 || idStr::Icmp( _name, "_default" ) == 0 ) {
1653 		declManager->MediaPrint( "DEFAULTED\n" );
1654 		return globalImages->defaultImage;
1655 	}
1656 
1657 	// strip any .tga file extensions from anywhere in the _name, including image program parameters
1658 	name = _name;
1659 	name.Replace( ".tga", "" );
1660 	name.BackSlashesToSlashes();
1661 
1662 	//
1663 	// look in loaded images
1664 	//
1665 	hash = name.FileNameHash();
1666 	for ( image = imageHashTable[hash]; image; image = image->hashNext ) {
1667 		if ( name.Icmp( image->imgName ) == 0 ) {
1668 			return image;
1669 		}
1670 	}
1671 
1672 	return NULL;
1673 }
1674 
1675 /*
1676 ===============
1677 PurgeAllImages
1678 ===============
1679 */
PurgeAllImages()1680 void idImageManager::PurgeAllImages() {
1681 	int		i;
1682 	idImage	*image;
1683 
1684 	for ( i = 0; i < images.Num() ; i++ ) {
1685 		image = images[i];
1686 		image->PurgeImage();
1687 	}
1688 }
1689 
1690 /*
1691 ===============
1692 ReloadAllImages
1693 ===============
1694 */
ReloadAllImages()1695 void idImageManager::ReloadAllImages() {
1696 	idCmdArgs args;
1697 
1698 	// build the compressed normal map palette
1699 	SetNormalPalette();
1700 
1701 	args.TokenizeString( "reloadImages reload", false );
1702 	R_ReloadImages_f( args );
1703 }
1704 
1705 /*
1706 ===============
1707 R_CombineCubeImages_f
1708 
1709 Used to combine animations of six separate tga files into
1710 a serials of 6x taller tga files, for preparation to roq compress
1711 ===============
1712 */
R_CombineCubeImages_f(const idCmdArgs & args)1713 void R_CombineCubeImages_f( const idCmdArgs &args ) {
1714 	if ( args.Argc() != 2 ) {
1715 		common->Printf( "usage: combineCubeImages <baseName>\n" );
1716 		common->Printf( " combines basename[1-6][0001-9999].tga to basenameCM[0001-9999].tga\n" );
1717 		common->Printf( " 1: forward 2:right 3:back 4:left 5:up 6:down\n" );
1718 		return;
1719 	}
1720 
1721 	idStr	baseName = args.Argv( 1 );
1722 	common->SetRefreshOnPrint( true );
1723 
1724 	for ( int frameNum = 1 ; frameNum < 10000 ; frameNum++ ) {
1725 		char	filename[MAX_IMAGE_NAME];
1726 		byte	*pics[6];
1727 		int		width, height;
1728 		int		side;
1729 		int		orderRemap[6] = { 1,3,4,2,5,6 };
1730 		for ( side = 0 ; side < 6 ; side++ ) {
1731 			sprintf( filename, "%s%i%04i.tga", baseName.c_str(), orderRemap[side], frameNum );
1732 
1733 			common->Printf( "reading %s\n", filename );
1734 			R_LoadImage( filename, &pics[side], &width, &height, NULL, true );
1735 
1736 			if ( !pics[side] ) {
1737 				common->Printf( "not found.\n" );
1738 				break;
1739 			}
1740 
1741 			// convert from "camera" images to native cube map images
1742 			switch( side ) {
1743 			case 0:	// forward
1744 				R_RotatePic( pics[side], width);
1745 				break;
1746 			case 1:	// back
1747 				R_RotatePic( pics[side], width);
1748 				R_HorizontalFlip( pics[side], width, height );
1749 				R_VerticalFlip( pics[side], width, height );
1750 				break;
1751 			case 2:	// left
1752 				R_VerticalFlip( pics[side], width, height );
1753 				break;
1754 			case 3:	// right
1755 				R_HorizontalFlip( pics[side], width, height );
1756 				break;
1757 			case 4:	// up
1758 				R_RotatePic( pics[side], width);
1759 				break;
1760 			case 5: // down
1761 				R_RotatePic( pics[side], width);
1762 				break;
1763 			}
1764 		}
1765 
1766 		if ( side != 6 ) {
1767 			for ( int i = 0 ; i < side ; side++ ) {
1768 				Mem_Free( pics[side] );
1769 			}
1770 			break;
1771 		}
1772 
1773 		byte	*combined = (byte *)Mem_Alloc( width*height*6*4 );
1774 		for (  side = 0 ; side < 6 ; side++ ) {
1775 			memcpy( combined+width*height*4*side, pics[side], width*height*4 );
1776 			Mem_Free( pics[side] );
1777 		}
1778 		sprintf( filename, "%sCM%04i.tga", baseName.c_str(), frameNum );
1779 
1780 		common->Printf( "writing %s\n", filename );
1781 		R_WriteTGA( filename, combined, width, height*6 );
1782 
1783 		Mem_Free( combined );
1784 	}
1785 	common->SetRefreshOnPrint( false );
1786 }
1787 
1788 
1789 /*
1790 ==================
1791 idImage::StartBackgroundImageLoad
1792 ==================
1793 */
StartBackgroundImageLoad()1794 void idImage::StartBackgroundImageLoad() {
1795 	if ( imageManager.numActiveBackgroundImageLoads >= idImageManager::MAX_BACKGROUND_IMAGE_LOADS ) {
1796 		return;
1797 	}
1798 	if ( globalImages->image_showBackgroundLoads.GetBool() ) {
1799 		common->Printf( "idImage::StartBackgroundImageLoad: %s\n", imgName.c_str() );
1800 	}
1801 	backgroundLoadInProgress = true;
1802 
1803 	if ( !precompressedFile ) {
1804 		common->Warning( "idImageManager::StartBackgroundImageLoad: %s wasn't a precompressed file", imgName.c_str() );
1805 		return;
1806 	}
1807 
1808 	bglNext = globalImages->backgroundImageLoads;
1809 	globalImages->backgroundImageLoads = this;
1810 
1811 	char	filename[MAX_IMAGE_NAME];
1812 	ImageProgramStringToCompressedFileName( imgName, filename );
1813 
1814 	bgl.completed = false;
1815 	bgl.f = fileSystem->OpenFileRead( filename );
1816 	if ( !bgl.f ) {
1817 		common->Warning( "idImageManager::StartBackgroundImageLoad: Couldn't load %s", imgName.c_str() );
1818 		return;
1819 	}
1820 	bgl.file.position = 0;
1821 	bgl.file.length = bgl.f->Length();
1822 	if ( bgl.file.length < sizeof( ddsFileHeader_t ) ) {
1823 		common->Warning( "idImageManager::StartBackgroundImageLoad: %s had a bad file length", imgName.c_str() );
1824 		return;
1825 	}
1826 
1827 	bgl.file.buffer = R_StaticAlloc( bgl.file.length );
1828 
1829 	fileSystem->BackgroundDownload( &bgl );
1830 
1831 	imageManager.numActiveBackgroundImageLoads++;
1832 
1833 	// purge some images if necessary
1834 	int		totalSize = 0;
1835 	for ( idImage *check = globalImages->cacheLRU.cacheUsageNext ; check != &globalImages->cacheLRU ; check = check->cacheUsageNext ) {
1836 		totalSize += check->StorageSize();
1837 	}
1838 	int	needed = this->StorageSize();
1839 
1840 	while ( ( totalSize + needed ) > globalImages->image_cacheMegs.GetFloat() * 1024 * 1024 ) {
1841 		// purge the least recently used
1842 		idImage	*check = globalImages->cacheLRU.cacheUsagePrev;
1843 		if ( check->texnum != TEXTURE_NOT_LOADED ) {
1844 			totalSize -= check->StorageSize();
1845 			if ( globalImages->image_showBackgroundLoads.GetBool() ) {
1846 				common->Printf( "purging %s\n", check->imgName.c_str() );
1847 			}
1848 			check->PurgeImage();
1849 		}
1850 		// remove it from the cached list
1851 		check->cacheUsageNext->cacheUsagePrev = check->cacheUsagePrev;
1852 		check->cacheUsagePrev->cacheUsageNext = check->cacheUsageNext;
1853 		check->cacheUsageNext = NULL;
1854 		check->cacheUsagePrev = NULL;
1855 	}
1856 }
1857 
1858 /*
1859 ==================
1860 R_CompleteBackgroundImageLoads
1861 
1862 Do we need to worry about vid_restarts here?
1863 ==================
1864 */
CompleteBackgroundImageLoads()1865 void idImageManager::CompleteBackgroundImageLoads() {
1866 	idImage	*remainingList = NULL;
1867 	idImage	*next;
1868 
1869 	for ( idImage *image = backgroundImageLoads ; image ; image = next ) {
1870 		next = image->bglNext;
1871 		if ( image->bgl.completed ) {
1872 			numActiveBackgroundImageLoads--;
1873 			fileSystem->CloseFile( image->bgl.f );
1874 			// upload the image
1875 			image->UploadPrecompressedImage( (byte *)image->bgl.file.buffer, image->bgl.file.length );
1876 			R_StaticFree( image->bgl.file.buffer );
1877 			if ( image_showBackgroundLoads.GetBool() ) {
1878 				common->Printf( "R_CompleteBackgroundImageLoad: %s\n", image->imgName.c_str() );
1879 			}
1880 		} else {
1881 			image->bglNext = remainingList;
1882 			remainingList = image;
1883 		}
1884 	}
1885 	if ( image_showBackgroundLoads.GetBool() ) {
1886 		static int prev;
1887 		if ( numActiveBackgroundImageLoads != prev ) {
1888 			prev = numActiveBackgroundImageLoads;
1889 			common->Printf( "background Loads: %i\n", numActiveBackgroundImageLoads );
1890 		}
1891 	}
1892 
1893 	backgroundImageLoads = remainingList;
1894 }
1895 
1896 /*
1897 ===============
1898 CheckCvars
1899 ===============
1900 */
CheckCvars()1901 void idImageManager::CheckCvars() {
1902 	// textureFilter stuff
1903 	if ( image_filter.IsModified() || image_anisotropy.IsModified() || image_lodbias.IsModified() ) {
1904 		ChangeTextureFilter();
1905 		image_filter.ClearModified();
1906 		image_anisotropy.ClearModified();
1907 		image_lodbias.ClearModified();
1908 	}
1909 }
1910 
1911 /*
1912 ===============
1913 SumOfUsedImages
1914 ===============
1915 */
SumOfUsedImages()1916 int idImageManager::SumOfUsedImages() {
1917 	int	total;
1918 	int i;
1919 	idImage	*image;
1920 
1921 	total = 0;
1922 	for ( i = 0; i < images.Num(); i++ ) {
1923 		image = images[i];
1924 		if ( image->frameUsed == backEnd.frameCount ) {
1925 			total += image->StorageSize();
1926 		}
1927 	}
1928 
1929 	return total;
1930 }
1931 
1932 /*
1933 ===============
1934 BindNull
1935 ===============
1936 */
BindNull()1937 void idImageManager::BindNull() {
1938 	tmu_t			*tmu;
1939 
1940 	tmu = &backEnd.glState.tmu[backEnd.glState.currenttmu];
1941 
1942 	if ( tmu->textureType == TT_CUBIC ) {
1943 		qglDisable( GL_TEXTURE_CUBE_MAP_EXT );
1944 	} else if ( tmu->textureType == TT_3D ) {
1945 		qglDisable( GL_TEXTURE_3D );
1946 	} else if ( tmu->textureType == TT_2D ) {
1947 		qglDisable( GL_TEXTURE_2D );
1948 	}
1949 	tmu->textureType = TT_DISABLED;
1950 }
1951 
1952 /*
1953 ===============
1954 Init
1955 ===============
1956 */
Init()1957 void idImageManager::Init() {
1958 
1959 	memset(imageHashTable, 0, sizeof(imageHashTable));
1960 
1961 	images.Resize( 1024, 1024 );
1962 
1963 	// clear the cached LRU
1964 	cacheLRU.cacheUsageNext = &cacheLRU;
1965 	cacheLRU.cacheUsagePrev = &cacheLRU;
1966 
1967 	// set default texture filter modes
1968 	ChangeTextureFilter();
1969 
1970 	// create built in images
1971 	defaultImage = ImageFromFunction( "_default", R_DefaultImage );
1972 	whiteImage = ImageFromFunction( "_white", R_WhiteImage );
1973 	blackImage = ImageFromFunction( "_black", R_BlackImage );
1974 	borderClampImage = ImageFromFunction( "_borderClamp", R_BorderClampImage );
1975 	flatNormalMap = ImageFromFunction( "_flat", R_FlatNormalImage );
1976 	ambientNormalMap = ImageFromFunction( "_ambient", R_AmbientNormalImage );
1977 	specularTableImage = ImageFromFunction( "_specularTable", R_SpecularTableImage );
1978 	specular2DTableImage = ImageFromFunction( "_specular2DTable", R_Specular2DTableImage );
1979 	rampImage = ImageFromFunction( "_ramp", R_RampImage );
1980 	alphaRampImage = ImageFromFunction( "_alphaRamp", R_RampImage );
1981 	alphaNotchImage = ImageFromFunction( "_alphaNotch", R_AlphaNotchImage );
1982 	fogImage = ImageFromFunction( "_fog", R_FogImage );
1983 	fogEnterImage = ImageFromFunction( "_fogEnter", R_FogEnterImage );
1984 	normalCubeMapImage = ImageFromFunction( "_normalCubeMap", makeNormalizeVectorCubeMap );
1985 	noFalloffImage = ImageFromFunction( "_noFalloff", R_CreateNoFalloffImage );
1986 	ImageFromFunction( "_quadratic", R_QuadraticImage );
1987 
1988 	// cinematicImage is used for cinematic drawing
1989 	// scratchImage is used for screen wipes/doublevision etc..
1990 	cinematicImage = ImageFromFunction("_cinematic", R_RGBA8Image );
1991 	scratchImage = ImageFromFunction("_scratch", R_RGBA8Image );
1992 	scratchImage2 = ImageFromFunction("_scratch2", R_RGBA8Image );
1993 	accumImage = ImageFromFunction("_accum", R_RGBA8Image );
1994 	scratchCubeMapImage = ImageFromFunction("_scratchCubeMap", makeNormalizeVectorCubeMap );
1995 	currentRenderImage = ImageFromFunction("_currentRender", R_RGBA8Image );
1996 
1997 	cmdSystem->AddCommand( "reloadImages", R_ReloadImages_f, CMD_FL_RENDERER, "reloads images" );
1998 	cmdSystem->AddCommand( "listImages", R_ListImages_f, CMD_FL_RENDERER, "lists images" );
1999 	cmdSystem->AddCommand( "combineCubeImages", R_CombineCubeImages_f, CMD_FL_RENDERER, "combines six images for roq compression" );
2000 
2001 	// should forceLoadImages be here?
2002 }
2003 
2004 /*
2005 ===============
2006 Shutdown
2007 ===============
2008 */
Shutdown()2009 void idImageManager::Shutdown() {
2010 	images.DeleteContents( true );
2011 }
2012 
2013 /*
2014 ====================
2015 BeginLevelLoad
2016 
2017 Mark all file based images as currently unused,
2018 but don't free anything.  Calls to ImageFromFile() will
2019 either mark the image as used, or create a new image without
2020 loading the actual data.
2021 ====================
2022 */
BeginLevelLoad()2023 void idImageManager::BeginLevelLoad() {
2024 	insideLevelLoad = true;
2025 
2026 	for ( int i = 0 ; i < images.Num() ; i++ ) {
2027 		idImage	*image = images[ i ];
2028 
2029 		// generator function images are always kept around
2030 		if ( image->generatorFunction ) {
2031 			continue;
2032 		}
2033 
2034 		if ( com_purgeAll.GetBool() ) {
2035 			image->PurgeImage();
2036 		}
2037 
2038 		image->levelLoadReferenced = false;
2039 	}
2040 }
2041 
2042 /*
2043 ====================
2044 EndLevelLoad
2045 
2046 Free all images marked as unused, and load all images that are necessary.
2047 This architecture prevents us from having the union of two level's
2048 worth of data present at one time.
2049 
2050 preload everything, never free
2051 preload everything, free unused after level load
2052 blocking load on demand
2053 preload low mip levels, background load remainder on demand
2054 ====================
2055 */
EndLevelLoad()2056 void idImageManager::EndLevelLoad() {
2057 	int			start = Sys_Milliseconds();
2058 
2059 	insideLevelLoad = false;
2060 	if ( idAsyncNetwork::serverDedicated.GetInteger() ) {
2061 		return;
2062 	}
2063 
2064 	common->Printf( "----- idImageManager::EndLevelLoad -----\n" );
2065 
2066 	int		purgeCount = 0;
2067 	int		keepCount = 0;
2068 	int		loadCount = 0;
2069 
2070 	// purge the ones we don't need
2071 	for ( int i = 0 ; i < images.Num() ; i++ ) {
2072 		idImage	*image = images[ i ];
2073 		if ( image->generatorFunction ) {
2074 			continue;
2075 		}
2076 
2077 		if ( !image->levelLoadReferenced && !image->referencedOutsideLevelLoad ) {
2078 //			common->Printf( "Purging %s\n", image->imgName.c_str() );
2079 			purgeCount++;
2080 			image->PurgeImage();
2081 		} else if ( image->texnum != idImage::TEXTURE_NOT_LOADED ) {
2082 //			common->Printf( "Keeping %s\n", image->imgName.c_str() );
2083 			keepCount++;
2084 		}
2085 	}
2086 
2087 	// load the ones we do need, if we are preloading
2088 	for ( int i = 0 ; i < images.Num() ; i++ ) {
2089 		idImage	*image = images[ i ];
2090 		if ( image->generatorFunction ) {
2091 			continue;
2092 		}
2093 
2094 		if ( image->levelLoadReferenced && image->texnum == idImage::TEXTURE_NOT_LOADED && !image->partialImage ) {
2095 //			common->Printf( "Loading %s\n", image->imgName.c_str() );
2096 			loadCount++;
2097 			image->ActuallyLoadImage( true, false );
2098 
2099 			if ( ( loadCount & 15 ) == 0 ) {
2100 				session->PacifierUpdate();
2101 			}
2102 		}
2103 	}
2104 
2105 	int	end = Sys_Milliseconds();
2106 	common->Printf( "%5i purged from previous\n", purgeCount );
2107 	common->Printf( "%5i kept from previous\n", keepCount );
2108 	common->Printf( "%5i new loaded\n", loadCount );
2109 	common->Printf( "all images loaded in %5.1f seconds\n", (end-start) * 0.001 );
2110 }
2111 
2112 /*
2113 ===============
2114 idImageManager::StartBuild
2115 ===============
2116 */
StartBuild()2117 void idImageManager::StartBuild() {
2118 	ddsList.Clear();
2119 	ddsHash.Free();
2120 }
2121 
2122 /*
2123 ===============
2124 idImageManager::FinishBuild
2125 ===============
2126 */
FinishBuild(bool removeDups)2127 void idImageManager::FinishBuild( bool removeDups ) {
2128 	idFile *batchFile;
2129 	if ( removeDups ) {
2130 		ddsList.Clear();
2131 		char *buffer = NULL;
2132 		fileSystem->ReadFile( "makedds.bat", (void**)&buffer );
2133 		if ( buffer ) {
2134 			idStr str = buffer;
2135 			while ( str.Length() ) {
2136 				int n = str.Find( '\n' );
2137 				if ( n > 0 ) {
2138 					idStr line = str.Left( n + 1 );
2139 					idStr right;
2140 					str.Right( str.Length() - n - 1, right );
2141 					str = right;
2142 					ddsList.AddUnique( line );
2143 				} else {
2144 					break;
2145 				}
2146 			}
2147 		}
2148 	}
2149 	batchFile = fileSystem->OpenFileWrite( ( removeDups ) ? "makedds2.bat" : "makedds.bat" );
2150 	if ( batchFile ) {
2151 		int i;
2152 		int ddsNum = ddsList.Num();
2153 
2154 		for ( i = 0; i < ddsNum; i++ ) {
2155 			batchFile->WriteFloatString( "%s", ddsList[ i ].c_str() );
2156 			batchFile->Printf( "@echo Finished compressing %d of %d.  %.1f percent done.\n", i+1, ddsNum, ((float)(i+1)/(float)ddsNum)*100.f );
2157 		}
2158 		fileSystem->CloseFile( batchFile );
2159 	}
2160 	ddsList.Clear();
2161 	ddsHash.Free();
2162 }
2163 
2164 /*
2165 ===============
2166 idImageManager::AddDDSCommand
2167 ===============
2168 */
AddDDSCommand(const char * cmd)2169 void idImageManager::AddDDSCommand( const char *cmd ) {
2170 	int i, key;
2171 
2172 	if ( !( cmd && *cmd ) ) {
2173 		return;
2174 	}
2175 
2176 	key = ddsHash.GenerateKey( cmd, false );
2177 	for ( i = ddsHash.First( key ); i != -1; i = ddsHash.Next( i ) ) {
2178 		if ( ddsList[i].Icmp( cmd ) == 0 ) {
2179 			break;
2180 		}
2181 	}
2182 
2183 	if ( i == -1 ) {
2184 		ddsList.Append( cmd );
2185 	}
2186 }
2187 
2188 /*
2189 ===============
2190 idImageManager::PrintMemInfo
2191 ===============
2192 */
PrintMemInfo(MemInfo_t * mi)2193 void idImageManager::PrintMemInfo( MemInfo_t *mi ) {
2194 	int i, j, total = 0;
2195 	int *sortIndex;
2196 	idFile *f;
2197 
2198 	f = fileSystem->OpenFileWrite( mi->filebase + "_images.txt" );
2199 	if ( !f ) {
2200 		return;
2201 	}
2202 
2203 	// sort first
2204 	sortIndex = new int[images.Num()];
2205 
2206 	for ( i = 0; i < images.Num(); i++ ) {
2207 		sortIndex[i] = i;
2208 	}
2209 
2210 	for ( i = 0; i < images.Num() - 1; i++ ) {
2211 		for ( j = i + 1; j < images.Num(); j++ ) {
2212 			if ( images[sortIndex[i]]->StorageSize() < images[sortIndex[j]]->StorageSize() ) {
2213 				int temp = sortIndex[i];
2214 				sortIndex[i] = sortIndex[j];
2215 				sortIndex[j] = temp;
2216 			}
2217 		}
2218 	}
2219 
2220 	// print next
2221 	for ( i = 0; i < images.Num(); i++ ) {
2222 		idImage *im = images[sortIndex[i]];
2223 		int size;
2224 
2225 		size = im->StorageSize();
2226 		total += size;
2227 
2228 		f->Printf( "%s %3i %s\n", idStr::FormatNumber( size ).c_str(), im->refCount, im->imgName.c_str() );
2229 	}
2230 
2231 	delete sortIndex;
2232 	mi->imageAssetsTotal = total;
2233 
2234 	f->Printf( "\nTotal image bytes allocated: %s\n", idStr::FormatNumber( total ).c_str() );
2235 	fileSystem->CloseFile( f );
2236 }
2237