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, ¤t );
1039 } else {
1040 // get the current values
1041 R_LoadImageProgram( imgName, NULL, NULL, NULL, ¤t );
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