doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Image_load.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "tr_local.h"
33 
34 /*
35 PROBLEM: compressed textures may break the zero clamp rule!
36 */
37 
38 static bool FormatIsDXT( int internalFormat ) {
39  if ( internalFormat < GL_COMPRESSED_RGB_S3TC_DXT1_EXT
40  || internalFormat > GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ) {
41  return false;
42  }
43  return true;
44 }
45 
46 int MakePowerOfTwo( int num ) {
47  int pot;
48  for (pot = 1 ; pot < num ; pot<<=1) {
49  }
50  return pot;
51 }
52 
53 /*
54 ================
55 BitsForInternalFormat
56 
57 Used for determining memory utilization
58 ================
59 */
60 int idImage::BitsForInternalFormat( int internalFormat ) const {
61  switch ( internalFormat ) {
62  case GL_INTENSITY8:
63  case 1:
64  return 8;
65  case 2:
66  case GL_LUMINANCE8_ALPHA8:
67  return 16;
68  case 3:
69  return 32; // on some future hardware, this may actually be 24, but be conservative
70  case 4:
71  return 32;
72  case GL_LUMINANCE8:
73  return 8;
74  case GL_ALPHA8:
75  return 8;
76  case GL_RGBA8:
77  return 32;
78  case GL_RGB8:
79  return 32; // on some future hardware, this may actually be 24, but be conservative
81  return 4;
83  return 4;
85  return 8;
87  return 8;
88  case GL_RGBA4:
89  return 16;
90  case GL_RGB5:
91  return 16;
93  return 8;
94  case GL_COLOR_INDEX:
95  return 8;
97  return 4; // not sure
99  return 8; // not sure
100  default:
101  common->Error( "R_BitsForInternalFormat: BAD FORMAT:%i", internalFormat );
102  }
103  return 0;
104 }
105 
106 /*
107 ==================
108 UploadCompressedNormalMap
109 
110 Create a 256 color palette to be used by compressed normal maps
111 ==================
112 */
113 void idImage::UploadCompressedNormalMap( int width, int height, const byte *rgba, int mipLevel ) {
114  byte *normals;
115  const byte *in;
116  byte *out;
117  int i, j;
118  int x, y, z;
119  int row;
120 
121  // OpenGL's pixel packing rule
122  row = width < 4 ? 4 : width;
123 
124  normals = (byte *)_alloca( row * height );
125  if ( !normals ) {
126  common->Error( "R_UploadCompressedNormalMap: _alloca failed" );
127  }
128 
129  in = rgba;
130  out = normals;
131  for ( i = 0 ; i < height ; i++, out += row, in += width * 4 ) {
132  for ( j = 0 ; j < width ; j++ ) {
133  x = in[ j * 4 + 0 ];
134  y = in[ j * 4 + 1 ];
135  z = in[ j * 4 + 2 ];
136 
137  int c;
138  if ( x == 128 && y == 128 && z == 128 ) {
139  // the "nullnormal" color
140  c = 255;
141  } else {
143  if ( c == 255 ) {
144  c = 254; // don't use the nullnormal color
145  }
146  }
147  out[j] = c;
148  }
149  }
150 
151  if ( mipLevel == 0 ) {
152  // Optionally write out the paletized normal map to a .tga
154  char filename[MAX_IMAGE_NAME];
156  char *ext = strrchr(filename, '.');
157  if ( ext ) {
158  strcpy(ext, "_pal.tga");
159  R_WritePalTGA( filename, normals, globalImages->compressedPalette, width, height);
160  }
161  }
162  }
163 
165  qglTexImage2D( GL_TEXTURE_2D,
166  mipLevel,
168  width,
169  height,
170  0,
171  GL_COLOR_INDEX,
172  GL_UNSIGNED_BYTE,
173  normals );
174  }
175 }
176 
177 
178 //=======================================================================
179 
180 
181 static byte mipBlendColors[16][4] = {
182  {0,0,0,0},
183  {255,0,0,128},
184  {0,255,0,128},
185  {0,0,255,128},
186  {255,0,0,128},
187  {0,255,0,128},
188  {0,0,255,128},
189  {255,0,0,128},
190  {0,255,0,128},
191  {0,0,255,128},
192  {255,0,0,128},
193  {0,255,0,128},
194  {0,0,255,128},
195  {255,0,0,128},
196  {0,255,0,128},
197  {0,0,255,128},
198 };
199 
200 /*
201 ===============
202 SelectInternalFormat
203 
204 This may need to scan six cube map images
205 ===============
206 */
207 GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, int width, int height,
208  textureDepth_t minimumDepth, bool *monochromeResult ) const {
209  int i, c;
210  const byte *scan;
211  int rgbOr, rgbAnd, aOr, aAnd;
212  int rgbDiffer, rgbaDiffer;
213 
214  // determine if the rgb channels are all the same
215  // and if either all rgb or all alpha are 255
216  c = width*height;
217  rgbDiffer = 0;
218  rgbaDiffer = 0;
219  rgbOr = 0;
220  rgbAnd = -1;
221  aOr = 0;
222  aAnd = -1;
223 
224  *monochromeResult = true; // until shown otherwise
225 
226  for ( int side = 0 ; side < numDataPtrs ; side++ ) {
227  scan = dataPtrs[side];
228  for ( i = 0; i < c; i++, scan += 4 ) {
229  int cor, cand;
230 
231  aOr |= scan[3];
232  aAnd &= scan[3];
233 
234  cor = scan[0] | scan[1] | scan[2];
235  cand = scan[0] & scan[1] & scan[2];
236 
237  // if rgb are all the same, the or and and will match
238  rgbDiffer |= ( cor ^ cand );
239 
240  // our "isMonochrome" test is more lax than rgbDiffer,
241  // allowing the values to be off by several units and
242  // still use the NV20 mono path
243  if ( *monochromeResult ) {
244  if ( abs( scan[0] - scan[1] ) > 16
245  || abs( scan[0] - scan[2] ) > 16 ) {
246  *monochromeResult = false;
247  }
248  }
249 
250  rgbOr |= cor;
251  rgbAnd &= cand;
252 
253  cor |= scan[3];
254  cand &= scan[3];
255 
256  rgbaDiffer |= ( cor ^ cand );
257  }
258  }
259 
260  // we assume that all 0 implies that the alpha channel isn't needed,
261  // because some tools will spit out 32 bit images with a 0 alpha instead
262  // of 255 alpha, but if the alpha actually is referenced, there will be
263  // different behavior in the compressed vs uncompressed states.
264  bool needAlpha;
265  if ( aAnd == 255 || aOr == 0 ) {
266  needAlpha = false;
267  } else {
268  needAlpha = true;
269  }
270 
271  // catch normal maps first
272  if ( minimumDepth == TD_BUMP ) {
274  // image_useNormalCompression should only be set to 1 on nv_10 and nv_20 paths
275  return GL_COLOR_INDEX8_EXT;
277  // image_useNormalCompression == 2 uses rxgb format which produces really good quality for medium settings
279  } else {
280  // we always need the alpha channel for bump maps for swizzling
281  return GL_RGBA8;
282  }
283  }
284 
285  // allow a complete override of image compression with a cvar
287  minimumDepth = TD_HIGH_QUALITY;
288  }
289 
290  if ( minimumDepth == TD_SPECULAR ) {
291  // we are assuming that any alpha channel is unintentional
294  } else {
295  return GL_RGB5;
296  }
297  }
298  if ( minimumDepth == TD_DIFFUSE ) {
299  // we might intentionally have an alpha channel for alpha tested textures
301  if ( !needAlpha ) {
303  } else {
305  }
306  } else if ( ( aAnd == 255 || aOr == 0 ) ) {
307  return GL_RGB5;
308  } else {
309  return GL_RGBA4;
310  }
311  }
312 
313  // there will probably be some drivers that don't
314  // correctly handle the intensity/alpha/luminance/luminance+alpha
315  // formats, so provide a fallback that only uses the rgb/rgba formats
317  // pretend rgb is varying and inconsistant, which
318  // prevents any of the more compact forms
319  rgbDiffer = 1;
320  rgbaDiffer = 1;
321  rgbAnd = 0;
322  }
323 
324  // cases without alpha
325  if ( !needAlpha ) {
326  if ( minimumDepth == TD_HIGH_QUALITY ) {
327  return GL_RGB8; // four bytes
328  }
330  return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // half byte
331  }
332  return GL_RGB5; // two bytes
333  }
334 
335  // cases with alpha
336  if ( !rgbaDiffer ) {
337  if ( minimumDepth != TD_HIGH_QUALITY && glConfig.textureCompressionAvailable ) {
338  return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
339  }
340  return GL_INTENSITY8; // single byte for all channels
341  }
342 
343 #if 0
344  // we don't support alpha textures any more, because there
345  // is a discrepancy in the definition of TEX_ENV_COMBINE that
346  // causes them to be treated as 0 0 0 A, instead of 1 1 1 A as
347  // normal texture modulation treats them
348  if ( rgbAnd == 255 ) {
349  return GL_ALPHA8; // single byte, only alpha
350  }
351 #endif
352 
353  if ( minimumDepth == TD_HIGH_QUALITY ) {
354  return GL_RGBA8; // four bytes
355  }
357  return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
358  }
359  if ( !rgbDiffer ) {
360  return GL_LUMINANCE8_ALPHA8; // two bytes, max quality
361  }
362  return GL_RGBA4; // two bytes
363 }
364 
365 /*
366 ==================
367 SetImageFilterAndRepeat
368 ==================
369 */
371  // set the minimize / maximize filtering
372  switch( filter ) {
373  case TF_DEFAULT:
374  qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
375  qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
376  break;
377  case TF_LINEAR:
378  qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
379  qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
380  break;
381  case TF_NEAREST:
382  qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
383  qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
384  break;
385  default:
386  common->FatalError( "R_CreateImage: bad texture filter" );
387  }
388 
390  // only do aniso filtering on mip mapped images
391  if ( filter == TF_DEFAULT ) {
393  } else {
395  }
396  }
399  }
400 
401  // set the wrap/clamp modes
402  switch( repeat ) {
403  case TR_REPEAT:
404  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
405  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
406  break;
407  case TR_CLAMP_TO_BORDER:
408  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
409  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
410  break;
411  case TR_CLAMP_TO_ZERO:
413  case TR_CLAMP:
414  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
415  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
416  break;
417  default:
418  common->FatalError( "R_CreateImage: bad texture repeat" );
419  }
420 }
421 
422 /*
423 ================
424 idImage::Downsize
425 helper function that takes the current width/height and might make them smaller
426 ================
427 */
428 void idImage::GetDownsize( int &scaled_width, int &scaled_height ) const {
429  int size = 0;
430 
431  // perform optional picmip operation to save texture memory
434  if ( size == 0 ) {
435  size = 64;
436  }
437  } else if ( depth == TD_BUMP && globalImages->image_downSizeBump.GetInteger() ) {
439  if ( size == 0 ) {
440  size = 64;
441  }
444  if ( size == 0 ) {
445  size = 256;
446  }
447  }
448 
449  if ( size > 0 ) {
450  while ( scaled_width > size || scaled_height > size ) {
451  if ( scaled_width > 1 ) {
452  scaled_width >>= 1;
453  }
454  if ( scaled_height > 1 ) {
455  scaled_height >>= 1;
456  }
457  }
458  }
459 
460  // clamp to minimum size
461  if ( scaled_width < 1 ) {
462  scaled_width = 1;
463  }
464  if ( scaled_height < 1 ) {
465  scaled_height = 1;
466  }
467 
468  // clamp size to the hardware specific upper limit
469  // scale both axis down equally so we don't have to
470  // deal with a half mip resampling
471  // This causes a 512*256 texture to sample down to
472  // 256*128 on a voodoo3, even though it could be 256*256
473  while ( scaled_width > glConfig.maxTextureSize
474  || scaled_height > glConfig.maxTextureSize ) {
475  scaled_width >>= 1;
476  scaled_height >>= 1;
477  }
478 }
479 
480 /*
481 ================
482 GenerateImage
483 
484 The alpha channel bytes should be 255 if you don't
485 want the channel.
486 
487 We need a material characteristic to ask for specific texture modes.
488 
489 Designed limitations of flexibility:
490 
491 No support for texture borders.
492 
493 No support for texture border color.
494 
495 No support for texture environment colors or GL_BLEND or GL_DECAL
496 texture environments, because the automatic optimization to single
497 or dual component textures makes those modes potentially undefined.
498 
499 No non-power-of-two images.
500 
501 No palettized textures.
502 
503 There is no way to specify separate wrap/clamp values for S and T
504 
505 There is no way to specify explicit mip map levels
506 
507 ================
508 */
509 void idImage::GenerateImage( const byte *pic, int width, int height,
510  textureFilter_t filterParm, bool allowDownSizeParm,
511  textureRepeat_t repeatParm, textureDepth_t depthParm ) {
512  bool preserveBorder;
513  byte *scaledBuffer;
514  int scaled_width, scaled_height;
515  byte *shrunk;
516 
517  PurgeImage();
518 
519  filter = filterParm;
520  allowDownSize = allowDownSizeParm;
521  repeat = repeatParm;
522  depth = depthParm;
523 
524  // if we don't have a rendering context, just return after we
525  // have filled in the parms. We must have the values set, or
526  // an image match from a shader before OpenGL starts would miss
527  // the generated texture
528  if ( !glConfig.isInitialized ) {
529  return;
530  }
531 
532  // don't let mip mapping smear the texture into the clamped border
533  if ( repeat == TR_CLAMP_TO_ZERO ) {
534  preserveBorder = true;
535  } else {
536  preserveBorder = false;
537  }
538 
539  // make sure it is a power of 2
540  scaled_width = MakePowerOfTwo( width );
541  scaled_height = MakePowerOfTwo( height );
542 
543  if ( scaled_width != width || scaled_height != height ) {
544  common->Error( "R_CreateImage: not a power of 2 image" );
545  }
546 
547  // Optionally modify our width/height based on options/hardware
548  GetDownsize( scaled_width, scaled_height );
549 
550  scaledBuffer = NULL;
551 
552  // generate the texture number
553  qglGenTextures( 1, &texnum );
554 
555  // select proper internal format before we resample
556  internalFormat = SelectInternalFormat( &pic, 1, width, height, depth, &isMonochrome );
557 
558  // copy or resample data as appropriate for first MIP level
559  if ( ( scaled_width == width ) && ( scaled_height == height ) ) {
560  // we must copy even if unchanged, because the border zeroing
561  // would otherwise modify const data
562  scaledBuffer = (byte *)R_StaticAlloc( sizeof( unsigned ) * scaled_width * scaled_height );
563  memcpy (scaledBuffer, pic, width*height*4);
564  } else {
565  // resample down as needed (FIXME: this doesn't seem like it resamples anymore!)
566  // scaledBuffer = R_ResampleTexture( pic, width, height, width >>= 1, height >>= 1 );
567  scaledBuffer = R_MipMap( pic, width, height, preserveBorder );
568  width >>= 1;
569  height >>= 1;
570  if ( width < 1 ) {
571  width = 1;
572  }
573  if ( height < 1 ) {
574  height = 1;
575  }
576 
577  while ( width > scaled_width || height > scaled_height ) {
578  shrunk = R_MipMap( scaledBuffer, width, height, preserveBorder );
579  R_StaticFree( scaledBuffer );
580  scaledBuffer = shrunk;
581 
582  width >>= 1;
583  height >>= 1;
584  if ( width < 1 ) {
585  width = 1;
586  }
587  if ( height < 1 ) {
588  height = 1;
589  }
590  }
591 
592  // one might have shrunk down below the target size
593  scaled_width = width;
594  scaled_height = height;
595  }
596 
597  uploadHeight = scaled_height;
598  uploadWidth = scaled_width;
599  type = TT_2D;
600 
601  // zero the border if desired, allowing clamped projection textures
602  // even after picmip resampling or careless artists.
603  if ( repeat == TR_CLAMP_TO_ZERO ) {
604  byte rgba[4];
605 
606  rgba[0] = rgba[1] = rgba[2] = 0;
607  rgba[3] = 255;
608  R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
609  }
610  if ( repeat == TR_CLAMP_TO_ZERO_ALPHA ) {
611  byte rgba[4];
612 
613  rgba[0] = rgba[1] = rgba[2] = 255;
614  rgba[3] = 0;
615  R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
616  }
617 
619  // Optionally write out the texture to a .tga
620  char filename[MAX_IMAGE_NAME];
622  char *ext = strrchr(filename, '.');
623  if ( ext ) {
624  strcpy( ext, ".tga" );
625  // swap the red/alpha for the write
626  /*
627  if ( depth == TD_BUMP ) {
628  for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
629  scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
630  scaledBuffer[ i + 3 ] = 0;
631  }
632  }
633  */
634  R_WriteTGA( filename, scaledBuffer, scaled_width, scaled_height, false );
635 
636  // put it back
637  /*
638  if ( depth == TD_BUMP ) {
639  for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
640  scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
641  scaledBuffer[ i ] = 0;
642  }
643  }
644  */
645  }
646  }
647 
648  // swap the red and alpha for rxgb support
649  // do this even on tga normal maps so we only have to use
650  // one fragment program
651  // if the image is precompressed ( either in palletized mode or true rxgb mode )
652  // then it is loaded above and the swap never happens here
654  for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
655  scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
656  scaledBuffer[ i ] = 0;
657  }
658  }
659  // upload the main image level
660  Bind();
661 
662 
663  if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
664  /*
665  if ( depth == TD_BUMP ) {
666  for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
667  scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
668  scaledBuffer[ i + 3 ] = 0;
669  }
670  }
671  */
672  UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, 0 );
673  } else {
674  qglTexImage2D( GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
675  }
676 
677  // create and upload the mip map levels, which we do in all cases, even if we don't think they are needed
678  int miplevel;
679 
680  miplevel = 0;
681  while ( scaled_width > 1 || scaled_height > 1 ) {
682  // preserve the border after mip map unless repeating
683  shrunk = R_MipMap( scaledBuffer, scaled_width, scaled_height, preserveBorder );
684  R_StaticFree( scaledBuffer );
685  scaledBuffer = shrunk;
686 
687  scaled_width >>= 1;
688  scaled_height >>= 1;
689  if ( scaled_width < 1 ) {
690  scaled_width = 1;
691  }
692  if ( scaled_height < 1 ) {
693  scaled_height = 1;
694  }
695  miplevel++;
696 
697  // this is a visualization tool that shades each mip map
698  // level with a different color so you can see the
699  // rasterizer's texture level selection algorithm
700  // Changing the color doesn't help with lumminance/alpha/intensity formats...
702  R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] );
703  }
704 
705  // upload the mip map
706  if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
707  UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, miplevel );
708  } else {
709  qglTexImage2D( GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height,
710  0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
711  }
712  }
713 
714  if ( scaledBuffer != 0 ) {
715  R_StaticFree( scaledBuffer );
716  }
717 
719 
720  // see if we messed anything up
721  GL_CheckErrors();
722 }
723 
724 
725 /*
726 ==================
727 Generate3DImage
728 ==================
729 */
730 void idImage::Generate3DImage( const byte *pic, int width, int height, int picDepth,
731  textureFilter_t filterParm, bool allowDownSizeParm,
732  textureRepeat_t repeatParm, textureDepth_t minDepthParm ) {
733  int scaled_width, scaled_height, scaled_depth;
734 
735  PurgeImage();
736 
737  filter = filterParm;
738  allowDownSize = allowDownSizeParm;
739  repeat = repeatParm;
740  depth = minDepthParm;
741 
742  // if we don't have a rendering context, just return after we
743  // have filled in the parms. We must have the values set, or
744  // an image match from a shader before OpenGL starts would miss
745  // the generated texture
746  if ( !glConfig.isInitialized ) {
747  return;
748  }
749 
750  // make sure it is a power of 2
751  scaled_width = MakePowerOfTwo( width );
752  scaled_height = MakePowerOfTwo( height );
753  scaled_depth = MakePowerOfTwo( picDepth );
754  if ( scaled_width != width || scaled_height != height || scaled_depth != picDepth ) {
755  common->Error( "R_Create3DImage: not a power of 2 image" );
756  }
757 
758  // FIXME: allow picmip here
759 
760  // generate the texture number
761  qglGenTextures( 1, &texnum );
762 
763  // select proper internal format before we resample
764  // this function doesn't need to know it is 3D, so just make it very "tall"
765  internalFormat = SelectInternalFormat( &pic, 1, width, height * picDepth, minDepthParm, &isMonochrome );
766 
767  uploadHeight = scaled_height;
768  uploadWidth = scaled_width;
769  uploadDepth = scaled_depth;
770 
771 
772  type = TT_3D;
773 
774  // upload the main image level
775  Bind();
776 
777  qglTexImage3D(GL_TEXTURE_3D, 0, internalFormat, scaled_width, scaled_height, scaled_depth,
778  0, GL_RGBA, GL_UNSIGNED_BYTE, pic );
779 
780  // create and upload the mip map levels
781  int miplevel;
782  byte *scaledBuffer, *shrunk;
783 
784  scaledBuffer = (byte *)R_StaticAlloc( scaled_width * scaled_height * scaled_depth * 4 );
785  memcpy( scaledBuffer, pic, scaled_width * scaled_height * scaled_depth * 4 );
786  miplevel = 0;
787  while ( scaled_width > 1 || scaled_height > 1 || scaled_depth > 1 ) {
788  // preserve the border after mip map unless repeating
789  shrunk = R_MipMap3D( scaledBuffer, scaled_width, scaled_height, scaled_depth,
790  (bool)(repeat != TR_REPEAT) );
791  R_StaticFree( scaledBuffer );
792  scaledBuffer = shrunk;
793 
794  scaled_width >>= 1;
795  scaled_height >>= 1;
796  scaled_depth >>= 1;
797  if ( scaled_width < 1 ) {
798  scaled_width = 1;
799  }
800  if ( scaled_height < 1 ) {
801  scaled_height = 1;
802  }
803  if ( scaled_depth < 1 ) {
804  scaled_depth = 1;
805  }
806  miplevel++;
807 
808  // upload the mip map
809  qglTexImage3D(GL_TEXTURE_3D, miplevel, internalFormat, scaled_width, scaled_height, scaled_depth,
810  0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
811  }
812  R_StaticFree( scaledBuffer );
813 
814  // set the minimize / maximize filtering
815  switch( filter ) {
816  case TF_DEFAULT:
817  qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
818  qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
819  break;
820  case TF_LINEAR:
821  qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
822  qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
823  break;
824  case TF_NEAREST:
825  qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
826  qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
827  break;
828  default:
829  common->FatalError( "R_CreateImage: bad texture filter" );
830  }
831 
832  // set the wrap/clamp modes
833  switch( repeat ) {
834  case TR_REPEAT:
835  qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT );
836  qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT );
838  break;
839  case TR_CLAMP_TO_BORDER:
840  qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
841  qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
842  break;
843  case TR_CLAMP_TO_ZERO:
845  case TR_CLAMP:
846  qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
847  qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
849  break;
850  default:
851  common->FatalError( "R_CreateImage: bad texture repeat" );
852  }
853 
854  // see if we messed anything up
855  GL_CheckErrors();
856 }
857 
858 
859 /*
860 ====================
861 GenerateCubeImage
862 
863 Non-square cube sides are not allowed
864 ====================
865 */
866 void idImage::GenerateCubeImage( const byte *pic[6], int size,
867  textureFilter_t filterParm, bool allowDownSizeParm,
868  textureDepth_t depthParm ) {
869  int scaled_width, scaled_height;
870  int width, height;
871  int i;
872 
873  PurgeImage();
874 
875  filter = filterParm;
876  allowDownSize = allowDownSizeParm;
877  depth = depthParm;
878 
879  type = TT_CUBIC;
880 
881  // if we don't have a rendering context, just return after we
882  // have filled in the parms. We must have the values set, or
883  // an image match from a shader before OpenGL starts would miss
884  // the generated texture
885  if ( !glConfig.isInitialized ) {
886  return;
887  }
888 
889  if ( ! glConfig.cubeMapAvailable ) {
890  return;
891  }
892 
893  width = height = size;
894 
895  // generate the texture number
896  qglGenTextures( 1, &texnum );
897 
898  // select proper internal format before we resample
899  internalFormat = SelectInternalFormat( pic, 6, width, height, depth, &isMonochrome );
900 
901  // don't bother with downsample for now
902  scaled_width = width;
903  scaled_height = height;
904 
905  uploadHeight = scaled_height;
906  uploadWidth = scaled_width;
907 
908  Bind();
909 
910  // no other clamp mode makes sense
913 
914  // set the minimize / maximize filtering
915  switch( filter ) {
916  case TF_DEFAULT:
919  break;
920  case TF_LINEAR:
921  qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
922  qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
923  break;
924  case TF_NEAREST:
925  qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
926  qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
927  break;
928  default:
929  common->FatalError( "R_CreateImage: bad texture filter" );
930  }
931 
932  // upload the base level
933  // FIXME: support GL_COLOR_INDEX8_EXT?
934  for ( i = 0 ; i < 6 ; i++ ) {
935  qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, internalFormat, scaled_width, scaled_height, 0,
936  GL_RGBA, GL_UNSIGNED_BYTE, pic[i] );
937  }
938 
939 
940  // create and upload the mip map levels
941  int miplevel;
942  byte *shrunk[6];
943 
944  for ( i = 0 ; i < 6 ; i++ ) {
945  shrunk[i] = R_MipMap( pic[i], scaled_width, scaled_height, false );
946  }
947 
948  miplevel = 1;
949  while ( scaled_width > 1 ) {
950  for ( i = 0 ; i < 6 ; i++ ) {
951  byte *shrunken;
952 
953  qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, miplevel, internalFormat,
954  scaled_width / 2, scaled_height / 2, 0,
955  GL_RGBA, GL_UNSIGNED_BYTE, shrunk[i] );
956 
957  if ( scaled_width > 2 ) {
958  shrunken = R_MipMap( shrunk[i], scaled_width/2, scaled_height/2, false );
959  } else {
960  shrunken = NULL;
961  }
962 
963  R_StaticFree( shrunk[i] );
964  shrunk[i] = shrunken;
965  }
966 
967  scaled_width >>= 1;
968  scaled_height >>= 1;
969  miplevel++;
970  }
971 
972  // see if we messed anything up
973  GL_CheckErrors();
974 }
975 
976 
977 /*
978 ================
979 ImageProgramStringToFileCompressedFileName
980 ================
981 */
982 void idImage::ImageProgramStringToCompressedFileName( const char *imageProg, char *fileName ) const {
983  const char *s;
984  char *f;
985 
986  strcpy( fileName, "dds/" );
987  f = fileName + strlen( fileName );
988 
989  int depth = 0;
990 
991  // convert all illegal characters to underscores
992  // this could conceivably produce a duplicated mapping, but we aren't going to worry about it
993  for ( s = imageProg ; *s ; s++ ) {
994  if ( *s == '/' || *s == '\\' || *s == '(') {
995  if ( depth < 4 ) {
996  *f = '/';
997  depth ++;
998  } else {
999  *f = ' ';
1000  }
1001  f++;
1002  } else if ( *s == '<' || *s == '>' || *s == ':' || *s == '|' || *s == '"' || *s == '.' ) {
1003  *f = '_';
1004  f++;
1005  } else if ( *s == ' ' && *(f-1) == '/' ) { // ignore a space right after a slash
1006  } else if ( *s == ')' || *s == ',' ) { // always ignore these
1007  } else {
1008  *f = *s;
1009  f++;
1010  }
1011  }
1012  *f++ = 0;
1013  strcat( fileName, ".dds" );
1014 }
1015 
1016 /*
1017 ==================
1018 NumLevelsForImageSize
1019 ==================
1020 */
1022  int numLevels = 1;
1023 
1024  while ( width > 1 || height > 1 ) {
1025  numLevels++;
1026  width >>= 1;
1027  height >>= 1;
1028  }
1029 
1030  return numLevels;
1031 }
1032 
1033 /*
1034 ================
1035 WritePrecompressedImage
1036 
1037 When we are happy with our source data, we can write out precompressed
1038 versions of everything to speed future load times.
1039 ================
1040 */
1042 
1043  // Always write the precompressed image if we're making a build
1044  if ( !com_makingBuild.GetBool() ) {
1046  return;
1047  }
1048  }
1049 
1050  if ( !glConfig.isInitialized ) {
1051  return;
1052  }
1053 
1054  char filename[MAX_IMAGE_NAME];
1056 
1057 
1058 
1059  int numLevels = NumLevelsForImageSize( uploadWidth, uploadHeight );
1060  if ( numLevels > MAX_TEXTURE_LEVELS ) {
1061  common->Warning( "R_WritePrecompressedImage: level > MAX_TEXTURE_LEVELS for image %s", filename );
1062  return;
1063  }
1064 
1065  // glGetTexImage only supports a small subset of all the available internal formats
1066  // We have to use BGRA because DDS is a windows based format
1067  int altInternalFormat = 0;
1068  int bitSize = 0;
1069  switch ( internalFormat ) {
1070  case GL_COLOR_INDEX8_EXT:
1071  case GL_COLOR_INDEX:
1072  // this will not work with dds viewers but we need it in this format to save disk
1073  // load speed ( i.e. size )
1074  altInternalFormat = GL_COLOR_INDEX;
1075  bitSize = 24;
1076  break;
1077  case 1:
1078  case GL_INTENSITY8:
1079  case GL_LUMINANCE8:
1080  case 3:
1081  case GL_RGB8:
1082  altInternalFormat = GL_BGR_EXT;
1083  bitSize = 24;
1084  break;
1085  case GL_LUMINANCE8_ALPHA8:
1086  case 4:
1087  case GL_RGBA8:
1088  altInternalFormat = GL_BGRA_EXT;
1089  bitSize = 32;
1090  break;
1091  case GL_ALPHA8:
1092  altInternalFormat = GL_ALPHA;
1093  bitSize = 8;
1094  break;
1095  default:
1096  if ( FormatIsDXT( internalFormat ) ) {
1097  altInternalFormat = internalFormat;
1098  } else {
1099  common->Warning("Unknown or unsupported format for %s", filename);
1100  return;
1101  }
1102  }
1103 
1104  if ( globalImages->image_useOffLineCompression.GetBool() && FormatIsDXT( altInternalFormat ) ) {
1105  idStr outFile = fileSystem->RelativePathToOSPath( filename, "fs_basepath" );
1106  idStr inFile = outFile;
1107  inFile.StripFileExtension();
1108  inFile.SetFileExtension( "tga" );
1109  idStr format;
1110  if ( depth == TD_BUMP ) {
1111  format = "RXGB +red 0.0 +green 0.5 +blue 0.5";
1112  } else {
1113  switch ( altInternalFormat ) {
1115  format = "DXT1";
1116  break;
1118  format = "DXT1 -alpha_threshold";
1119  break;
1121  format = "DXT3";
1122  break;
1124  format = "DXT5";
1125  break;
1126  }
1127  }
1128  globalImages->AddDDSCommand( va( "z:/d3xp/compressonator/thecompressonator -convert \"%s\" \"%s\" %s -mipmaps\n", inFile.c_str(), outFile.c_str(), format.c_str() ) );
1129  return;
1130  }
1131 
1132 
1133  ddsFileHeader_t header;
1134  memset( &header, 0, sizeof(header) );
1135  header.dwSize = sizeof(header);
1137  header.dwHeight = uploadHeight;
1138  header.dwWidth = uploadWidth;
1139 
1140  // hack in our monochrome flag for the NV20 optimization
1141  if ( isMonochrome ) {
1142  header.dwFlags |= DDSF_ID_MONOCHROME;
1143  }
1144 
1145  if ( FormatIsDXT( altInternalFormat ) ) {
1146  // size (in bytes) of the compressed base image
1147  header.dwFlags |= DDSF_LINEARSIZE;
1148  header.dwPitchOrLinearSize = ( ( uploadWidth + 3 ) / 4 ) * ( ( uploadHeight + 3 ) / 4 )*
1149  (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1150  }
1151  else {
1152  // 4 Byte aligned line width (from nv_dds)
1153  header.dwFlags |= DDSF_PITCH;
1154  header.dwPitchOrLinearSize = ( ( uploadWidth * bitSize + 31 ) & -32 ) >> 3;
1155  }
1156 
1157  header.dwCaps1 = DDSF_TEXTURE;
1158 
1159  if ( numLevels > 1 ) {
1160  header.dwMipMapCount = numLevels;
1161  header.dwFlags |= DDSF_MIPMAPCOUNT;
1162  header.dwCaps1 |= DDSF_MIPMAP | DDSF_COMPLEX;
1163  }
1164 
1165  header.ddspf.dwSize = sizeof(header.ddspf);
1166  if ( FormatIsDXT( altInternalFormat ) ) {
1167  header.ddspf.dwFlags = DDSF_FOURCC;
1168  switch ( altInternalFormat ) {
1170  header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
1171  break;
1173  header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
1174  header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
1175  break;
1177  header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','3');
1178  break;
1180  header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','5');
1181  break;
1182  }
1183  } else {
1184  header.ddspf.dwFlags = ( internalFormat == GL_COLOR_INDEX8_EXT ) ? DDSF_RGB | DDSF_ID_INDEXCOLOR : DDSF_RGB;
1185  header.ddspf.dwRGBBitCount = bitSize;
1186  switch ( altInternalFormat ) {
1187  case GL_BGRA_EXT:
1188  case GL_LUMINANCE_ALPHA:
1189  header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
1190  header.ddspf.dwABitMask = 0xFF000000;
1191  // Fall through
1192  case GL_BGR_EXT:
1193  case GL_LUMINANCE:
1194  case GL_COLOR_INDEX:
1195  header.ddspf.dwRBitMask = 0x00FF0000;
1196  header.ddspf.dwGBitMask = 0x0000FF00;
1197  header.ddspf.dwBBitMask = 0x000000FF;
1198  break;
1199  case GL_ALPHA:
1200  header.ddspf.dwFlags = DDSF_ALPHAPIXELS;
1201  header.ddspf.dwABitMask = 0xFF000000;
1202  break;
1203  default:
1204  common->Warning( "Unknown or unsupported format for %s", filename );
1205  return;
1206  }
1207  }
1208 
1209  idFile *f = fileSystem->OpenFileWrite( filename );
1210  if ( f == NULL ) {
1211  common->Warning( "Could not open %s trying to write precompressed image", filename );
1212  return;
1213  }
1214  common->Printf( "Writing precompressed image: %s\n", filename );
1215 
1216  f->Write( "DDS ", 4 );
1217  f->Write( &header, sizeof(header) );
1218 
1219  // bind to the image so we can read back the contents
1220  Bind();
1221 
1222  qglPixelStorei( GL_PACK_ALIGNMENT, 1 ); // otherwise small rows get padded to 32 bits
1223 
1224  int uw = uploadWidth;
1225  int uh = uploadHeight;
1226 
1227  // Will be allocated first time through the loop
1228  byte *data = NULL;
1229 
1230  for ( int level = 0 ; level < numLevels ; level++ ) {
1231 
1232  int size = 0;
1233  if ( FormatIsDXT( altInternalFormat ) ) {
1234  size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
1235  (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1236  } else {
1237  size = uw * uh * (bitSize / 8);
1238  }
1239 
1240  if (data == NULL) {
1241  data = (byte *)R_StaticAlloc( size );
1242  }
1243 
1244  if ( FormatIsDXT( altInternalFormat ) ) {
1245  qglGetCompressedTexImageARB( GL_TEXTURE_2D, level, data );
1246  } else {
1247  qglGetTexImage( GL_TEXTURE_2D, level, altInternalFormat, GL_UNSIGNED_BYTE, data );
1248  }
1249 
1250  f->Write( data, size );
1251 
1252  uw /= 2;
1253  uh /= 2;
1254  if (uw < 1) {
1255  uw = 1;
1256  }
1257  if (uh < 1) {
1258  uh = 1;
1259  }
1260  }
1261 
1262  if (data != NULL) {
1263  R_StaticFree( data );
1264  }
1265 
1266  fileSystem->CloseFile( f );
1267 }
1268 
1269 /*
1270 ================
1271 ShouldImageBePartialCached
1272 
1273 Returns true if there is a precompressed image, and it is large enough
1274 to be worth caching
1275 ================
1276 */
1279  return false;
1280  }
1281 
1282  if ( !globalImages->image_useCache.GetBool() ) {
1283  return false;
1284  }
1285 
1286  // the allowDownSize flag does double-duty as don't-partial-load
1287  if ( !allowDownSize ) {
1288  return false;
1289  }
1290 
1291  if ( globalImages->image_cacheMinK.GetInteger() <= 0 ) {
1292  return false;
1293  }
1294 
1295  // if we are doing a copyFiles, make sure the original images are referenced
1296  if ( fileSystem->PerformingCopyFiles() ) {
1297  return false;
1298  }
1299 
1300  char filename[MAX_IMAGE_NAME];
1302 
1303  // get the file timestamp
1304  fileSystem->ReadFile( filename, NULL, &timestamp );
1305 
1306  if ( timestamp == FILE_NOT_FOUND_TIMESTAMP ) {
1307  return false;
1308  }
1309 
1310  // open it and get the file size
1311  idFile *f;
1312 
1313  f = fileSystem->OpenFileRead( filename );
1314  if ( !f ) {
1315  return false;
1316  }
1317 
1318  int len = f->Length();
1319  fileSystem->CloseFile( f );
1320 
1321  if ( len <= globalImages->image_cacheMinK.GetInteger() * 1024 ) {
1322  return false;
1323  }
1324 
1325  // we do want to do a partial load
1326  return true;
1327 }
1328 
1329 /*
1330 ================
1331 CheckPrecompressedImage
1332 
1333 If fullLoad is false, only the small mip levels of the image will be loaded
1334 ================
1335 */
1336 bool idImage::CheckPrecompressedImage( bool fullLoad ) {
1338  return false;
1339  }
1340 
1341 #if 1 // ( _D3XP had disabled ) - Allow grabbing of DDS's from original Doom pak files
1342  // if we are doing a copyFiles, make sure the original images are referenced
1343  if ( fileSystem->PerformingCopyFiles() ) {
1344  return false;
1345  }
1346 #endif
1347 
1349  return false;
1350  }
1351 
1352  // god i love last minute hacks :-)
1353  if ( com_machineSpec.GetInteger() >= 1 && com_videoRam.GetInteger() >= 128 && imgName.Icmpn( "lights/", 7 ) == 0 ) {
1354  return false;
1355  }
1356 
1357  char filename[MAX_IMAGE_NAME];
1359 
1360  // get the file timestamp
1361  ID_TIME_T precompTimestamp;
1362  fileSystem->ReadFile( filename, NULL, &precompTimestamp );
1363 
1364 
1365  if ( precompTimestamp == FILE_NOT_FOUND_TIMESTAMP ) {
1366  return false;
1367  }
1368 
1369  if ( !generatorFunction && timestamp != FILE_NOT_FOUND_TIMESTAMP ) {
1370  if ( precompTimestamp < timestamp ) {
1371  // The image has changed after being precompressed
1372  return false;
1373  }
1374  }
1375 
1376  timestamp = precompTimestamp;
1377 
1378  // open it and just read the header
1379  idFile *f;
1380 
1381  f = fileSystem->OpenFileRead( filename );
1382  if ( !f ) {
1383  return false;
1384  }
1385 
1386  int len = f->Length();
1387  if ( len < sizeof( ddsFileHeader_t ) ) {
1388  fileSystem->CloseFile( f );
1389  return false;
1390  }
1391 
1392  if ( !fullLoad && len > globalImages->image_cacheMinK.GetInteger() * 1024 ) {
1393  len = globalImages->image_cacheMinK.GetInteger() * 1024;
1394  }
1395 
1396  byte *data = (byte *)R_StaticAlloc( len );
1397 
1398  f->Read( data, len );
1399 
1400  fileSystem->CloseFile( f );
1401 
1402  unsigned long magic = LittleLong( *(unsigned long *)data );
1403  ddsFileHeader_t *_header = (ddsFileHeader_t *)(data + 4);
1404  int ddspf_dwFlags = LittleLong( _header->ddspf.dwFlags );
1405 
1406  if ( magic != DDS_MAKEFOURCC('D', 'D', 'S', ' ')) {
1407  common->Printf( "CheckPrecompressedImage( %s ): magic != 'DDS '\n", imgName.c_str() );
1408  R_StaticFree( data );
1409  return false;
1410  }
1411 
1412  // if we don't support color index textures, we must load the full image
1413  // should we just expand the 256 color image to 32 bit for upload?
1414  if ( ddspf_dwFlags & DDSF_ID_INDEXCOLOR && !glConfig.sharedTexturePaletteAvailable ) {
1415  R_StaticFree( data );
1416  return false;
1417  }
1418 
1419  // upload all the levels
1420  UploadPrecompressedImage( data, len );
1421 
1422  R_StaticFree( data );
1423 
1424  return true;
1425 }
1426 
1427 /*
1428 ===================
1429 UploadPrecompressedImage
1430 
1431 This can be called by the front end during nromal loading,
1432 or by the backend after a background read of the file
1433 has completed
1434 ===================
1435 */
1437  ddsFileHeader_t *header = (ddsFileHeader_t *)(data + 4);
1438 
1439  // ( not byte swapping dwReserved1 dwReserved2 )
1440  header->dwSize = LittleLong( header->dwSize );
1441  header->dwFlags = LittleLong( header->dwFlags );
1442  header->dwHeight = LittleLong( header->dwHeight );
1443  header->dwWidth = LittleLong( header->dwWidth );
1444  header->dwPitchOrLinearSize = LittleLong( header->dwPitchOrLinearSize );
1445  header->dwDepth = LittleLong( header->dwDepth );
1446  header->dwMipMapCount = LittleLong( header->dwMipMapCount );
1447  header->dwCaps1 = LittleLong( header->dwCaps1 );
1448  header->dwCaps2 = LittleLong( header->dwCaps2 );
1449 
1450  header->ddspf.dwSize = LittleLong( header->ddspf.dwSize );
1451  header->ddspf.dwFlags = LittleLong( header->ddspf.dwFlags );
1452  header->ddspf.dwFourCC = LittleLong( header->ddspf.dwFourCC );
1453  header->ddspf.dwRGBBitCount = LittleLong( header->ddspf.dwRGBBitCount );
1454  header->ddspf.dwRBitMask = LittleLong( header->ddspf.dwRBitMask );
1455  header->ddspf.dwGBitMask = LittleLong( header->ddspf.dwGBitMask );
1456  header->ddspf.dwBBitMask = LittleLong( header->ddspf.dwBBitMask );
1457  header->ddspf.dwABitMask = LittleLong( header->ddspf.dwABitMask );
1458 
1459  // generate the texture number
1460  qglGenTextures( 1, &texnum );
1461 
1462  int externalFormat = 0;
1463 
1464  precompressedFile = true;
1465 
1466  uploadWidth = header->dwWidth;
1467  uploadHeight = header->dwHeight;
1468  if ( header->ddspf.dwFlags & DDSF_FOURCC ) {
1469  switch ( header->ddspf.dwFourCC ) {
1470  case DDS_MAKEFOURCC( 'D', 'X', 'T', '1' ):
1471  if ( header->ddspf.dwFlags & DDSF_ALPHAPIXELS ) {
1472  internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1473  } else {
1474  internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1475  }
1476  break;
1477  case DDS_MAKEFOURCC( 'D', 'X', 'T', '3' ):
1478  internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
1479  break;
1480  case DDS_MAKEFOURCC( 'D', 'X', 'T', '5' ):
1481  internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1482  break;
1483  case DDS_MAKEFOURCC( 'R', 'X', 'G', 'B' ):
1484  internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1485  break;
1486  default:
1487  common->Warning( "Invalid compressed internal format\n" );
1488  return;
1489  }
1490  } else if ( ( header->ddspf.dwFlags & DDSF_RGBA ) && header->ddspf.dwRGBBitCount == 32 ) {
1491  externalFormat = GL_BGRA_EXT;
1492  internalFormat = GL_RGBA8;
1493  } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 32 ) {
1494  externalFormat = GL_BGRA_EXT;
1495  internalFormat = GL_RGBA8;
1496  } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 24 ) {
1497  if ( header->ddspf.dwFlags & DDSF_ID_INDEXCOLOR ) {
1498  externalFormat = GL_COLOR_INDEX;
1499  internalFormat = GL_COLOR_INDEX8_EXT;
1500  } else {
1501  externalFormat = GL_BGR_EXT;
1502  internalFormat = GL_RGB8;
1503  }
1504  } else if ( header->ddspf.dwRGBBitCount == 8 ) {
1505  externalFormat = GL_ALPHA;
1506  internalFormat = GL_ALPHA8;
1507  } else {
1508  common->Warning( "Invalid uncompressed internal format\n" );
1509  return;
1510  }
1511 
1512  // we need the monochrome flag for the NV20 optimized path
1513  if ( header->dwFlags & DDSF_ID_MONOCHROME ) {
1514  isMonochrome = true;
1515  }
1516 
1517  type = TT_2D; // FIXME: we may want to support pre-compressed cube maps in the future
1518 
1519  Bind();
1520 
1521  int numMipmaps = 1;
1522  if ( header->dwFlags & DDSF_MIPMAPCOUNT ) {
1523  numMipmaps = header->dwMipMapCount;
1524  }
1525 
1526  int uw = uploadWidth;
1527  int uh = uploadHeight;
1528 
1529  // We may skip some mip maps if we are downsizing
1530  int skipMip = 0;
1532 
1533  byte *imagedata = data + sizeof(ddsFileHeader_t) + 4;
1534 
1535  for ( int i = 0 ; i < numMipmaps; i++ ) {
1536  int size = 0;
1537  if ( FormatIsDXT( internalFormat ) ) {
1538  size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
1539  (internalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1540  } else {
1541  size = uw * uh * (header->ddspf.dwRGBBitCount / 8);
1542  }
1543 
1544  if ( uw > uploadWidth || uh > uploadHeight ) {
1545  skipMip++;
1546  } else {
1547  if ( FormatIsDXT( internalFormat ) ) {
1548  qglCompressedTexImage2DARB( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, size, imagedata );
1549  } else {
1550  qglTexImage2D( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, externalFormat, GL_UNSIGNED_BYTE, imagedata );
1551  }
1552  }
1553 
1554  imagedata += size;
1555  uw /= 2;
1556  uh /= 2;
1557  if (uw < 1) {
1558  uw = 1;
1559  }
1560  if (uh < 1) {
1561  uh = 1;
1562  }
1563  }
1564 
1566 }
1567 
1568 /*
1569 ===============
1570 ActuallyLoadImage
1571 
1572 Absolutely every image goes through this path
1573 On exit, the idImage will have a valid OpenGL texture number that can be bound
1574 ===============
1575 */
1576 void idImage::ActuallyLoadImage( bool checkForPrecompressed, bool fromBackEnd ) {
1577  int width, height;
1578  byte *pic;
1579 
1580  // this is the ONLY place generatorFunction will ever be called
1581  if ( generatorFunction ) {
1582  generatorFunction( this );
1583  return;
1584  }
1585 
1586  // if we are a partial image, we are only going to load from a compressed file
1587  if ( isPartialImage ) {
1588  if ( CheckPrecompressedImage( false ) ) {
1589  return;
1590  }
1591  // this is an error -- the partial image failed to load
1592  MakeDefault();
1593  return;
1594  }
1595 
1596  //
1597  // load the image from disk
1598  //
1599  if ( cubeFiles != CF_2D ) {
1600  byte *pics[6];
1601 
1602  // we don't check for pre-compressed cube images currently
1603  R_LoadCubeImages( imgName, cubeFiles, pics, &width, &timestamp );
1604 
1605  if ( pics[0] == NULL ) {
1606  common->Warning( "Couldn't load cube image: %s", imgName.c_str() );
1607  MakeDefault();
1608  return;
1609  }
1610 
1611  GenerateCubeImage( (const byte **)pics, width, filter, allowDownSize, depth );
1612  precompressedFile = false;
1613 
1614  for ( int i = 0 ; i < 6 ; i++ ) {
1615  if ( pics[i] ) {
1616  R_StaticFree( pics[i] );
1617  }
1618  }
1619  } else {
1620  // see if we have a pre-generated image file that is
1621  // already image processed and compressed
1622  if ( checkForPrecompressed && globalImages->image_usePrecompressedTextures.GetBool() ) {
1623  if ( CheckPrecompressedImage( true ) ) {
1624  // we got the precompressed image
1625  return;
1626  }
1627  // fall through to load the normal image
1628  }
1629 
1630  R_LoadImageProgram( imgName, &pic, &width, &height, &timestamp, &depth );
1631 
1632  if ( pic == NULL ) {
1633  common->Warning( "Couldn't load image: %s", imgName.c_str() );
1634  MakeDefault();
1635  return;
1636  }
1637 /*
1638  // swap the red and alpha for rxgb support
1639  // do this even on tga normal maps so we only have to use
1640  // one fragment program
1641  // if the image is precompressed ( either in palletized mode or true rxgb mode )
1642  // then it is loaded above and the swap never happens here
1643  if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 1 ) {
1644  for ( int i = 0; i < width * height * 4; i += 4 ) {
1645  pic[ i + 3 ] = pic[ i ];
1646  pic[ i ] = 0;
1647  }
1648  }
1649 */
1650  // build a hash for checking duplicate image files
1651  // NOTE: takes about 10% of image load times (SD)
1652  // may not be strictly necessary, but some code uses it, so let's leave it in
1653  imageHash = MD4_BlockChecksum( pic, width * height * 4 );
1654 
1655  GenerateImage( pic, width, height, filter, allowDownSize, repeat, depth );
1656  timestamp = timestamp;
1657  precompressedFile = false;
1658 
1659  R_StaticFree( pic );
1660 
1661  // write out the precompressed version of this file if needed
1663  }
1664 }
1665 
1666 //=========================================================================================================
1667 
1668 /*
1669 ===============
1670 PurgeImage
1671 ===============
1672 */
1674  if ( texnum != TEXTURE_NOT_LOADED ) {
1675  // sometimes is NULL when exiting with an error
1676  if ( qglDeleteTextures ) {
1677  qglDeleteTextures( 1, &texnum ); // this should be the ONLY place it is ever called!
1678  }
1680  }
1681 
1682  // clear all the current binding caches, so the next bind will do a real one
1683  for ( int i = 0 ; i < MAX_MULTITEXTURE_UNITS ; i++ ) {
1687  }
1688 }
1689 
1690 /*
1691 ==============
1692 Bind
1693 
1694 Automatically enables 2D mapping, cube mapping, or 3D texturing if needed
1695 ==============
1696 */
1698  if ( tr.logFile ) {
1699  RB_LogComment( "idImage::Bind( %s )\n", imgName.c_str() );
1700  }
1701 
1702  // if this is an image that we are caching, move it to the front of the LRU chain
1703  if ( partialImage ) {
1704  if ( cacheUsageNext ) {
1705  // unlink from old position
1708  }
1709  // link in at the head of the list
1712 
1715  }
1716 
1717  // load the image if necessary (FIXME: not SMP safe!)
1718  if ( texnum == TEXTURE_NOT_LOADED ) {
1719  if ( partialImage ) {
1720  // if we have a partial image, go ahead and use that
1721  this->partialImage->Bind();
1722 
1723  // start a background load of the full thing if it isn't already in the queue
1724  if ( !backgroundLoadInProgress ) {
1726  }
1727  return;
1728  }
1729 
1730  // load the image on demand here, which isn't our normal game operating mode
1731  ActuallyLoadImage( true, true ); // check for precompressed, load is from back end
1732  }
1733 
1734 
1735  // bump our statistic counters
1737  bindCount++;
1738 
1740 
1741  // enable or disable apropriate texture modes
1743  if ( tmu->textureType == TT_CUBIC ) {
1745  } else if ( tmu->textureType == TT_3D ) {
1747  } else if ( tmu->textureType == TT_2D ) {
1748  qglDisable( GL_TEXTURE_2D );
1749  }
1750 
1751  if ( type == TT_CUBIC ) {
1753  } else if ( type == TT_3D ) {
1755  } else if ( type == TT_2D ) {
1756  qglEnable( GL_TEXTURE_2D );
1757  }
1758  tmu->textureType = type;
1759  }
1760 
1761  // bind the texture
1762  if ( type == TT_2D ) {
1763  if ( tmu->current2DMap != texnum ) {
1764  tmu->current2DMap = texnum;
1765  qglBindTexture( GL_TEXTURE_2D, texnum );
1766  }
1767  } else if ( type == TT_CUBIC ) {
1768  if ( tmu->currentCubeMap != texnum ) {
1769  tmu->currentCubeMap = texnum;
1771  }
1772  } else if ( type == TT_3D ) {
1773  if ( tmu->current3DMap != texnum ) {
1774  tmu->current3DMap = texnum;
1776  }
1777  }
1778 
1779  if ( com_purgeAll.GetBool() ) {
1780  GLclampf priority = 1.0f;
1781  qglPrioritizeTextures( 1, &texnum, &priority );
1782  }
1783 }
1784 
1785 /*
1786 ==============
1787 BindFragment
1788 
1789 Fragment programs explicitly say which type of map they want, so we don't need to
1790 do any enable / disable changes
1791 ==============
1792 */
1794  if ( tr.logFile ) {
1795  RB_LogComment( "idImage::BindFragment %s )\n", imgName.c_str() );
1796  }
1797 
1798  // if this is an image that we are caching, move it to the front of the LRU chain
1799  if ( partialImage ) {
1800  if ( cacheUsageNext ) {
1801  // unlink from old position
1804  }
1805  // link in at the head of the list
1808 
1811  }
1812 
1813  // load the image if necessary (FIXME: not SMP safe!)
1814  if ( texnum == TEXTURE_NOT_LOADED ) {
1815  if ( partialImage ) {
1816  // if we have a partial image, go ahead and use that
1817  this->partialImage->BindFragment();
1818 
1819  // start a background load of the full thing if it isn't already in the queue
1820  if ( !backgroundLoadInProgress ) {
1822  }
1823  return;
1824  }
1825 
1826  // load the image on demand here, which isn't our normal game operating mode
1827  ActuallyLoadImage( true, true ); // check for precompressed, load is from back end
1828  }
1829 
1830 
1831  // bump our statistic counters
1833  bindCount++;
1834 
1835  // bind the texture
1836  if ( type == TT_2D ) {
1837  qglBindTexture( GL_TEXTURE_2D, texnum );
1838  } else if ( type == TT_RECT ) {
1840  } else if ( type == TT_CUBIC ) {
1842  } else if ( type == TT_3D ) {
1844  }
1845 }
1846 
1847 
1848 /*
1849 ====================
1850 CopyFramebuffer
1851 ====================
1852 */
1853 void idImage::CopyFramebuffer( int x, int y, int imageWidth, int imageHeight, bool useOversizedBuffer ) {
1854  Bind();
1855 
1856  if ( cvarSystem->GetCVarBool( "g_lowresFullscreenFX" ) ) {
1857  imageWidth = 512;
1858  imageHeight = 512;
1859  }
1860 
1861  // if the size isn't a power of 2, the image must be increased in size
1862  int potWidth, potHeight;
1863 
1864  potWidth = MakePowerOfTwo( imageWidth );
1865  potHeight = MakePowerOfTwo( imageHeight );
1866 
1867  GetDownsize( imageWidth, imageHeight );
1868  GetDownsize( potWidth, potHeight );
1869 
1870  qglReadBuffer( GL_BACK );
1871 
1872  // only resize if the current dimensions can't hold it at all,
1873  // otherwise subview renderings could thrash this
1874  if ( ( useOversizedBuffer && ( uploadWidth < potWidth || uploadHeight < potHeight ) )
1875  || ( !useOversizedBuffer && ( uploadWidth != potWidth || uploadHeight != potHeight ) ) ) {
1876  uploadWidth = potWidth;
1877  uploadHeight = potHeight;
1878  if ( potWidth == imageWidth && potHeight == imageHeight ) {
1879  qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, x, y, imageWidth, imageHeight, 0 );
1880  } else {
1881  byte *junk;
1882  // we need to create a dummy image with power of two dimensions,
1883  // then do a qglCopyTexSubImage2D of the data we want
1884  // this might be a 16+ meg allocation, which could fail on _alloca
1885  junk = (byte *)Mem_Alloc( potWidth * potHeight * 4 );
1886  memset( junk, 0, potWidth * potHeight * 4 );
1887 #if 0 // Disabling because it's unnecessary and introduces a green strip on edge of _currentRender
1888  for ( int i = 0 ; i < potWidth * potHeight * 4 ; i+=4 ) {
1889  junk[i+1] = 255;
1890  }
1891 #endif
1892  qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, potWidth, potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, junk );
1893  Mem_Free( junk );
1894 
1895  qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1896  }
1897  } else {
1898  // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1899  // it and don't try and do a texture compression or some other silliness
1900  qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1901  }
1902 
1903  // if the image isn't a full power of two, duplicate an extra row and/or column to fix bilerps
1904  if ( imageWidth != potWidth ) {
1905  qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, imageWidth, 0, x+imageWidth-1, y, 1, imageHeight );
1906  }
1907  if ( imageHeight != potHeight ) {
1908  qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, imageHeight, x, y+imageHeight-1, imageWidth, 1 );
1909  }
1910 
1911  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1912  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1913 
1914  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1915  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
1916 
1918 }
1919 
1920 /*
1921 ====================
1922 CopyDepthbuffer
1923 
1924 This should just be part of copyFramebuffer once we have a proper image type field
1925 ====================
1926 */
1927 void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight ) {
1928  Bind();
1929 
1930  // if the size isn't a power of 2, the image must be increased in size
1931  int potWidth, potHeight;
1932 
1933  potWidth = MakePowerOfTwo( imageWidth );
1934  potHeight = MakePowerOfTwo( imageHeight );
1935 
1936  if ( uploadWidth != potWidth || uploadHeight != potHeight ) {
1937  uploadWidth = potWidth;
1938  uploadHeight = potHeight;
1939  if ( potWidth == imageWidth && potHeight == imageHeight ) {
1940  qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, 0 );
1941  } else {
1942  // we need to create a dummy image with power of two dimensions,
1943  // then do a qglCopyTexSubImage2D of the data we want
1944  qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
1945  qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1946  }
1947  } else {
1948  // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1949  // it and don't try and do a texture compression or some other silliness
1950  qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1951  }
1952 
1953 // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
1954 // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
1955 
1956  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1957  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
1958 }
1959 
1960 /*
1961 =============
1962 RB_UploadScratchImage
1963 
1964 if rows = cols * 6, assume it is a cube map animation
1965 =============
1966 */
1967 void idImage::UploadScratch( const byte *data, int cols, int rows ) {
1968  int i;
1969 
1970  // if rows = cols * 6, assume it is a cube map animation
1971  if ( rows == cols * 6 ) {
1972  if ( type != TT_CUBIC ) {
1973  type = TT_CUBIC;
1974  uploadWidth = -1; // for a non-sub upload
1975  }
1976 
1977  Bind();
1978 
1979  rows /= 6;
1980  // if the scratchImage isn't in the format we want, specify it as a new texture
1981  if ( cols != uploadWidth || rows != uploadHeight ) {
1982  uploadWidth = cols;
1983  uploadHeight = rows;
1984 
1985  // upload the base level
1986  for ( i = 0 ; i < 6 ; i++ ) {
1987  qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, GL_RGB8, cols, rows, 0,
1988  GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
1989  }
1990  } else {
1991  // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1992  // it and don't try and do a texture compression
1993  for ( i = 0 ; i < 6 ; i++ ) {
1994  qglTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, 0, 0, cols, rows,
1995  GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
1996  }
1997  }
1998  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1999  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
2000  // no other clamp mode makes sense
2003  } else {
2004  // otherwise, it is a 2D image
2005  if ( type != TT_2D ) {
2006  type = TT_2D;
2007  uploadWidth = -1; // for a non-sub upload
2008  }
2009 
2010  Bind();
2011 
2012  // if the scratchImage isn't in the format we want, specify it as a new texture
2013  if ( cols != uploadWidth || rows != uploadHeight ) {
2014  uploadWidth = cols;
2015  uploadHeight = rows;
2016  qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
2017  } else {
2018  // otherwise, just subimage upload it so that drivers can tell we are going to be changing
2019  // it and don't try and do a texture compression
2020  qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
2021  }
2022  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
2023  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
2024  // these probably should be clamp, but we have a lot of issues with editor
2025  // geometry coming out with texcoords slightly off one side, resulting in
2026  // a smear across the entire polygon
2027 #if 1
2028  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
2029  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
2030 #else
2031  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
2032  qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
2033 #endif
2034  }
2035 }
2036 
2037 
2039  classification = tag;
2040 }
2041 
2042 /*
2043 ==================
2044 StorageSize
2045 ==================
2046 */
2048  int baseSize;
2049 
2050  if ( texnum == TEXTURE_NOT_LOADED ) {
2051  return 0;
2052  }
2053 
2054  switch ( type ) {
2055  default:
2056  case TT_2D:
2057  baseSize = uploadWidth*uploadHeight;
2058  break;
2059  case TT_3D:
2060  baseSize = uploadWidth*uploadHeight*uploadDepth;
2061  break;
2062  case TT_CUBIC:
2063  baseSize = 6 * uploadWidth*uploadHeight;
2064  break;
2065  }
2066 
2067  baseSize *= BitsForInternalFormat( internalFormat );
2068 
2069  baseSize /= 8;
2070 
2071  // account for mip mapping
2072  baseSize = baseSize * 4 / 3;
2073 
2074  return baseSize;
2075 }
2076 
2077 /*
2078 ==================
2079 Print
2080 ==================
2081 */
2082 void idImage::Print() const {
2083  if ( precompressedFile ) {
2084  common->Printf( "P" );
2085  } else if ( generatorFunction ) {
2086  common->Printf( "F" );
2087  } else {
2088  common->Printf( " " );
2089  }
2090 
2091  switch ( type ) {
2092  case TT_2D:
2093  common->Printf( " " );
2094  break;
2095  case TT_3D:
2096  common->Printf( "3" );
2097  break;
2098  case TT_CUBIC:
2099  common->Printf( "C" );
2100  break;
2101  case TT_RECT:
2102  common->Printf( "R" );
2103  break;
2104  default:
2105  common->Printf( "<BAD TYPE:%i>", type );
2106  break;
2107  }
2108 
2109  common->Printf( "%4i %4i ", uploadWidth, uploadHeight );
2110 
2111  switch( filter ) {
2112  case TF_DEFAULT:
2113  common->Printf( "dflt " );
2114  break;
2115  case TF_LINEAR:
2116  common->Printf( "linr " );
2117  break;
2118  case TF_NEAREST:
2119  common->Printf( "nrst " );
2120  break;
2121  default:
2122  common->Printf( "<BAD FILTER:%i>", filter );
2123  break;
2124  }
2125 
2126  switch ( internalFormat ) {
2127  case GL_INTENSITY8:
2128  case 1:
2129  common->Printf( "I " );
2130  break;
2131  case 2:
2132  case GL_LUMINANCE8_ALPHA8:
2133  common->Printf( "LA " );
2134  break;
2135  case 3:
2136  common->Printf( "RGB " );
2137  break;
2138  case 4:
2139  common->Printf( "RGBA " );
2140  break;
2141  case GL_LUMINANCE8:
2142  common->Printf( "L " );
2143  break;
2144  case GL_ALPHA8:
2145  common->Printf( "A " );
2146  break;
2147  case GL_RGBA8:
2148  common->Printf( "RGBA8 " );
2149  break;
2150  case GL_RGB8:
2151  common->Printf( "RGB8 " );
2152  break;
2154  common->Printf( "DXT1 " );
2155  break;
2157  common->Printf( "DXT1A " );
2158  break;
2160  common->Printf( "DXT3 " );
2161  break;
2163  common->Printf( "DXT5 " );
2164  break;
2165  case GL_RGBA4:
2166  common->Printf( "RGBA4 " );
2167  break;
2168  case GL_RGB5:
2169  common->Printf( "RGB5 " );
2170  break;
2171  case GL_COLOR_INDEX8_EXT:
2172  common->Printf( "CI8 " );
2173  break;
2174  case GL_COLOR_INDEX:
2175  common->Printf( "CI " );
2176  break;
2177  case GL_COMPRESSED_RGB_ARB:
2178  common->Printf( "RGBC " );
2179  break;
2181  common->Printf( "RGBAC " );
2182  break;
2183  case 0:
2184  common->Printf( " " );
2185  break;
2186  default:
2187  common->Printf( "<BAD FORMAT:%i>", internalFormat );
2188  break;
2189  }
2190 
2191  switch ( repeat ) {
2192  case TR_REPEAT:
2193  common->Printf( "rept " );
2194  break;
2195  case TR_CLAMP_TO_ZERO:
2196  common->Printf( "zero " );
2197  break;
2199  common->Printf( "azro " );
2200  break;
2201  case TR_CLAMP:
2202  common->Printf( "clmp " );
2203  break;
2204  default:
2205  common->Printf( "<BAD REPEAT:%i>", repeat );
2206  break;
2207  }
2208 
2209  common->Printf( "%4ik ", StorageSize() / 1024 );
2210 
2211  common->Printf( " %s\n", imgName.c_str() );
2212 }
GLenum
Definition: qgl.h:120
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
Definition: glext.h:1874
unsigned long dwCaps1
Definition: Image.h:115
const unsigned long DDSF_ALPHAPIXELS
Definition: Image.h:77
bool ShouldImageBePartialCached()
#define GL_BGR_EXT
Definition: glext.h:1497
virtual idFile * OpenFileRead(const char *relativePath, bool allowCopyFiles=true, const char *gamedir=NULL)=0
virtual bool PerformingCopyFiles(void) const =0
idStr & SetFileExtension(const char *extension)
Definition: Str.cpp:743
Definition: Image.h:133
void MakeDefault()
Definition: Image_init.cpp:252
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
const unsigned long DDSF_LINEARSIZE
Definition: Image.h:73
#define qglDisable
Definition: qgl_linked.h:92
void GenerateImage(const byte *pic, int width, int height, textureFilter_t filter, bool allowDownSize, textureRepeat_t repeat, textureDepth_t depth)
Definition: Image_load.cpp:509
#define GL_TEXTURE_LOD_BIAS_EXT
Definition: glext.h:1745
void GL_CheckErrors(void)
bool isMonochrome
Definition: Image.h:241
static idCVar image_useCompression
Definition: Image.h:365
void GetDownsize(int &scaled_width, int &scaled_height) const
Definition: Image_load.cpp:428
GLenum GLsizei GLenum format
Definition: glext.h:2846
idImage * cacheUsageNext
Definition: Image.h:252
int internalFormat
Definition: Image.h:250
GLenum textureMinFilter
Definition: Image.h:430
const unsigned long DDSF_RGB
Definition: Image.h:79
bool anisotropicAvailable
Definition: RenderSystem.h:64
virtual int ReadFile(const char *relativePath, void **buffer, ID_TIME_T *timestamp=NULL)=0
bool textureLODBiasAvailable
Definition: RenderSystem.h:65
idCVar com_purgeAll("com_purgeAll","0", CVAR_BOOL|CVAR_ARCHIVE|CVAR_SYSTEM,"purge everything between level loads")
int c_copyFrameBuffer
Definition: tr_local.h:665
#define qglBindTexture
Definition: qgl_linked.h:34
GLenum SelectInternalFormat(const byte **dataPtrs, int numDataPtrs, int width, int height, textureDepth_t minimumDepth, bool *monochromeResult) const
Definition: Image_load.cpp:207
PFNGLCOMPRESSEDTEXIMAGE2DARBPROC qglCompressedTexImage2DARB
#define GL_BGRA_EXT
Definition: glext.h:1498
GLenum GLint GLint y
Definition: glext.h:2849
#define MAX_IMAGE_NAME
Definition: Image.h:144
Definition: Image.h:139
static idCVar image_downSizeBump
Definition: Image.h:386
idFileSystem * fileSystem
Definition: FileSystem.cpp:500
idCVar com_machineSpec("com_machineSpec","-1", CVAR_INTEGER|CVAR_ARCHIVE|CVAR_SYSTEM,"hardware classification, -1 = not detected, 0 = low quality, 1 = medium quality, 2 = high quality, 3 = ultra quality")
textureRepeat_t
Definition: Material.h:52
const unsigned long DDSF_PITCH
Definition: Image.h:70
int maxTextureSize
Definition: RenderSystem.h:54
#define GL_CLAMP_TO_BORDER
Definition: glext.h:253
void Print() const
byte * R_MipMap(const byte *in, int width, int height, bool preserveBorder)
#define GL_TEXTURE_WRAP_R
Definition: glext.h:74
cubeFiles_t cubeFiles
Definition: Image.h:235
unsigned long dwDepth
Definition: Image.h:111
GLint GLint GLsizei GLsizei GLsizei depth
Definition: glext.h:2878
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
Definition: glext.h:1873
const unsigned long DDSF_MIPMAP
Definition: Image.h:89
const unsigned long DDSF_CAPS
Definition: Image.h:67
void Generate3DImage(const byte *pic, int width, int height, int depth, textureFilter_t filter, bool allowDownSize, textureRepeat_t repeat, textureDepth_t minDepth)
Definition: Image_load.cpp:730
float textureLODBias
Definition: Image.h:433
GLuint GLuint GLsizei GLenum type
Definition: glext.h:2845
#define qglDeleteTextures
Definition: qgl_linked.h:88
#define GL_CLAMP_TO_EDGE
Definition: glext.h:87
ddsFilePixelFormat_t ddspf
Definition: Image.h:114
unsigned long dwMipMapCount
Definition: Image.h:112
void SetClassification(int tag)
GLdouble s
Definition: glext.h:2935
GLenum GLsizei len
Definition: glext.h:3472
void R_LoadImageProgram(const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp, textureDepth_t *depth=NULL)
GLenum GLint x
Definition: glext.h:2849
int i
Definition: process.py:33
int currenttmu
Definition: tr_local.h:605
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT
Definition: glext.h:1872
void PurgeImage()
GLuint GLuint num
Definition: glext.h:5390
bool isPartialImage
Definition: Image.h:223
#define GL_COMPRESSED_RGB_ARB
Definition: glext.h:456
bool sharedTexturePaletteAvailable
Definition: RenderSystem.h:72
byte * R_MipMap3D(const byte *in, int width, int height, int depth, bool preserveBorder)
#define qglEnable
Definition: qgl_linked.h:101
const int MAX_MULTITEXTURE_UNITS
Definition: tr_local.h:602
unsigned long dwPitchOrLinearSize
Definition: Image.h:110
#define qglTexSubImage2D
Definition: qgl_linked.h:336
unsigned long dwGBitMask
Definition: Image.h:99
#define GL_TEXTURE_MAX_ANISOTROPY_EXT
Definition: glext.h:1749
void CopyFramebuffer(int x, int y, int width, int height, bool useOversizedBuffer)
bool precompressedFile
Definition: Image.h:239
unsigned long dwRGBBitCount
Definition: Image.h:97
float textureAnisotropy
Definition: Image.h:432
backEndState_t backEnd
Definition: tr_backend.cpp:35
bool backgroundLoadInProgress
Definition: Image.h:224
#define qglGetTexImage
Definition: qgl_linked.h:152
int currentCubeMap
Definition: tr_local.h:597
void R_BlendOverTexture(byte *data, int pixelCount, const byte blend[4])
int Icmpn(const char *text, int n) const
Definition: Str.h:672
Definition: File.h:50
void GenerateCubeImage(const byte *pic[6], int size, textureFilter_t filter, bool allowDownSize, textureDepth_t depth)
Definition: Image_load.cpp:866
idCVar com_makingBuild("com_makingBuild","0", CVAR_BOOL|CVAR_SYSTEM,"1 when making a build")
const unsigned long DDSF_HEIGHT
Definition: Image.h:68
unsigned long dwFourCC
Definition: Image.h:96
static idCVar image_forceDownSize
Definition: Image.h:383
ID_TIME_T timestamp
Definition: Image.h:242
const GLubyte * c
Definition: glext.h:4677
const unsigned long DDSF_FOURCC
Definition: Image.h:78
#define DDS_MAKEFOURCC(a, b, c, d)
Definition: Image.h:91
idStr & StripFileExtension(void)
Definition: Str.cpp:757
#define qglReadBuffer
Definition: qgl_linked.h:265
GLuint texnum
Definition: Image.h:216
textureType_t type
Definition: Image.h:217
const unsigned long DDSF_MIPMAPCOUNT
Definition: Image.h:72
const unsigned long DDSF_WIDTH
Definition: Image.h:69
bool allowDownSize
Definition: Image.h:231
void UploadScratch(const byte *pic, int width, int height)
unsigned long dwABitMask
Definition: Image.h:101
idCommon * common
Definition: Common.cpp:206
void ActuallyLoadImage(bool checkForPrecompressed, bool fromBackEnd)
void R_WritePalTGA(const char *filename, const byte *data, const byte *palette, int width, int height, bool flipVertical=false)
#define GL_COLOR_INDEX8_EXT
Definition: glext.h:1347
#define NULL
Definition: Lib.h:88
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT
Definition: glext.h:1725
Definition: Image.h:135
idImage cacheLRU
Definition: Image.h:438
static idCVar image_downSizeBumpLimit
Definition: Image.h:387
static idCVar image_usePrecompressedTextures
Definition: Image.h:370
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
int bindCount
Definition: Image.h:219
int GetInteger(void) const
Definition: CVarSystem.h:143
glstate_t glState
Definition: tr_local.h:663
void UploadCompressedNormalMap(int width, int height, const byte *rgba, int mipLevel)
Definition: Image_load.cpp:113
const unsigned long DDSF_ID_MONOCHROME
Definition: Image.h:84
int frameUsed
Definition: Image.h:218
idImageManager * globalImages
Definition: Image_init.cpp:74
const unsigned long DDSF_RGBA
Definition: Image.h:80
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
virtual idFile * OpenFileWrite(const char *relativePath, const char *basePath="fs_savepath")=0
void StartBackgroundImageLoad()
static idCVar image_useNormalCompression
Definition: Image.h:375
virtual int Read(void *buffer, int len)
Definition: File.cpp:179
void R_WriteTGA(const char *filename, const byte *data, int width, int height, bool flipVertical=false)
Definition: Image_files.cpp:85
void SetImageFilterAndRepeat() const
Definition: Image_load.cpp:370
#define qglTexParameterf
Definition: qgl_linked.h:331
static idCVar image_writeTGA
Definition: Image.h:374
unsigned long dwWidth
Definition: Image.h:109
void Mem_Free(void *ptr)
Definition: Heap.cpp:1087
unsigned long MD4_BlockChecksum(const void *data, int length)
Definition: MD4.cpp:247
unsigned long dwHeight
Definition: Image.h:108
#define qglCopyTexImage2D
Definition: qgl_linked.h:83
GLenum GLsizei width
Definition: glext.h:2846
static idCVar image_downSizeSpecular
Definition: Image.h:384
static idCVar image_writeNormalTGA
Definition: Image.h:372
virtual void Printf(const char *fmt,...) id_attribute((format(printf
static idCVar image_downSize
Definition: Image.h:364
int LittleLong(int l)
Definition: Lib.cpp:281
PFNGLGETCOMPRESSEDTEXIMAGEARBPROC qglGetCompressedTexImageARB
byte originalToCompressed[256]
Definition: Image.h:426
unsigned long dwSize
Definition: Image.h:106
GLenum GLsizei GLsizei height
Definition: glext.h:2856
static idCVar image_downSizeLimit
Definition: Image.h:389
const unsigned long DDSF_TEXTURE
Definition: Image.h:88
void R_SetBorderTexels(byte *inBase, int width, int height, const byte border[4])
int uploadHeight
Definition: Image.h:249
virtual const char * RelativePathToOSPath(const char *relativePath, const char *basePath="fs_devpath")=0
void ImageProgramStringToCompressedFileName(const char *imageProg, char *fileName) const
Definition: Image_load.cpp:982
void UploadPrecompressedImage(byte *data, int len)
static idCVar image_cacheMinK
Definition: Image.h:378
unsigned long dwBBitMask
Definition: Image.h:100
void(* generatorFunction)(idImage *image)
Definition: Image.h:230
int uploadWidth
Definition: Image.h:249
bool cubeMapAvailable
Definition: RenderSystem.h:69
void RB_LogComment(const char *comment,...)
Definition: tr_backend.cpp:112
#define GL_COMPRESSED_RGBA_ARB
Definition: glext.h:457
Definition: Image.h:126
void BindFragment()
#define qglCopyTexSubImage2D
Definition: qgl_linked.h:85
bool textureCompressionAvailable
Definition: RenderSystem.h:63
textureType_t textureType
Definition: tr_local.h:599
const unsigned long DDSF_ID_INDEXCOLOR
Definition: Image.h:83
GLenum GLenum GLvoid * row
Definition: glext.h:2866
static idCVar image_writeNormalTGAPalletized
Definition: Image.h:373
bool GetBool(void) const
Definition: CVarSystem.h:142
glconfig_t glConfig
GLenum filter
Definition: glext.h:3704
tuple f
Definition: idal.py:89
#define qglTexImage2D
Definition: qgl_linked.h:330
GLuint in
Definition: glext.h:5388
#define qglTexParameteri
Definition: qgl_linked.h:333
unsigned char byte
Definition: Lib.h:75
void * R_StaticAlloc(int bytes)
Definition: tr_main.cpp:301
idCVar com_videoRam("com_videoRam","64", CVAR_INTEGER|CVAR_SYSTEM|CVAR_NOCHEAT|CVAR_ARCHIVE,"holds the last amount of detected video ram")
int classification
Definition: Image.h:246
GLenum textureMaxFilter
Definition: Image.h:431
static idCVar image_useAllFormats
Definition: Image.h:369
GLsizeiptr size
Definition: glext.h:3112
unsigned long dwFlags
Definition: Image.h:95
#define GL_TEXTURE_3D
Definition: glext.h:71
virtual int Write(const void *buffer, int len)
Definition: File.cpp:189
Definition: Str.h:116
int imageHash
Definition: Image.h:244
int maxTextureUnits
Definition: RenderSystem.h:55
#define qglPixelStorei
Definition: qgl_linked.h:224
idImage * cacheUsagePrev
Definition: Image.h:252
const char * c_str(void) const
Definition: Str.h:487
unsigned long dwFlags
Definition: Image.h:107
idStr imgName
Definition: Image.h:229
static const int TEXTURE_NOT_LOADED
Definition: Image.h:215
idRenderSystemLocal tr
virtual bool GetCVarBool(const char *name) const =0
#define GL_TEXTURE_CUBE_MAP_EXT
Definition: glext.h:1723
#define qglGenTextures
Definition: qgl_linked.h:127
int current2DMap
Definition: tr_local.h:595
void AddDDSCommand(const char *cmd)
unsigned long dwSize
Definition: Image.h:94
static idCVar image_useCache
Definition: Image.h:381
int current3DMap
Definition: tr_local.h:596
int MakePowerOfTwo(int num)
Definition: Image_load.cpp:46
void * Mem_Alloc(const int size)
Definition: Heap.cpp:1067
GLint j
Definition: qgl.h:264
GLint level
Definition: glext.h:2878
bool isInitialized
Definition: RenderSystem.h:98
GLenum internalFormat
Definition: glext.h:4176
if(!ValidDisplayID(prefInfo.prefDisplayID)) prefInfo.prefDisplayID
unsigned long dwCaps2
Definition: Image.h:116
int StorageSize() const
unsigned long dwRBitMask
Definition: Image.h:98
char * va(const char *fmt,...)
Definition: Str.cpp:1568
virtual void CloseFile(idFile *f)=0
void R_StaticFree(void *data)
Definition: tr_main.cpp:335
int BitsForInternalFormat(int internalFormat) const
Definition: Image_load.cpp:60
virtual void Error(const char *fmt,...) id_attribute((format(printf
bool CheckPrecompressedImage(bool fullLoad)
#define GL_TEXTURE_RECTANGLE_NV
Definition: glext.h:2053
void Bind()
int uploadDepth
Definition: Image.h:249
int NumLevelsForImageSize(int width, int height) const
void WritePrecompressedImage()
void CopyDepthbuffer(int x, int y, int width, int height)
textureRepeat_t repeat
Definition: Image.h:233
GLdouble GLdouble z
Definition: glext.h:3067
bool R_LoadCubeImages(const char *cname, cubeFiles_t extensions, byte *pic[6], int *size, ID_TIME_T *timestamp)
virtual void virtual void Warning(const char *fmt,...) id_attribute((format(printf
textureFilter_t
Definition: Material.h:46
Definition: Image.h:132
static idCVar image_useOffLineCompression
Definition: Image.h:376
static idCVar image_colorMipLevels
Definition: Image.h:363
static idCVar image_writePrecompressedTextures
Definition: Image.h:371
virtual int Length(void)
Definition: File.cpp:199
idImage * partialImage
Definition: Image.h:222
const unsigned long DDSF_COMPLEX
Definition: Image.h:87
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
Definition: glext.h:1875
static idCVar image_downSizeSpecularLimit
Definition: Image.h:385
const unsigned long DDSF_PIXELFORMAT
Definition: Image.h:71
textureDepth_t
Definition: Image.h:122
#define qglPrioritizeTextures
Definition: qgl_linked.h:236
tmu_t tmu[MAX_MULTITEXTURE_UNITS]
Definition: tr_local.h:604
byte compressedPalette[768]
Definition: Image.h:427