1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 // tr_image.c
23 #include "tr_local.h"
24
25 static byte s_intensitytable[256];
26 static unsigned char s_gammatable[256];
27
28 int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
29 int gl_filter_max = GL_LINEAR;
30
31 #define FILE_HASH_SIZE 1024
32 static image_t* hashTable[FILE_HASH_SIZE];
33
34 /*
35 ** R_GammaCorrect
36 */
R_GammaCorrect(byte * buffer,int bufSize)37 void R_GammaCorrect( byte *buffer, int bufSize ) {
38 int i;
39
40 for ( i = 0; i < bufSize; i++ ) {
41 buffer[i] = s_gammatable[buffer[i]];
42 }
43 }
44
45 typedef struct {
46 char *name;
47 int minimize, maximize;
48 } textureMode_t;
49
50 textureMode_t modes[] = {
51 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
52 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
53 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
54 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
55 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
56 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
57 };
58
59 /*
60 ================
61 return a hash value for the filename
62 ================
63 */
generateHashValue(const char * fname)64 static long generateHashValue( const char *fname ) {
65 int i;
66 long hash;
67 char letter;
68
69 hash = 0;
70 i = 0;
71 while (fname[i] != '\0') {
72 letter = tolower(fname[i]);
73 if (letter =='.') break; // don't include extension
74 if (letter =='\\') letter = '/'; // damn path names
75 hash+=(long)(letter)*(i+119);
76 i++;
77 }
78 hash &= (FILE_HASH_SIZE-1);
79 return hash;
80 }
81
82 /*
83 ===============
84 GL_TextureMode
85 ===============
86 */
GL_TextureMode(const char * string)87 void GL_TextureMode( const char *string ) {
88 int i;
89 image_t *glt;
90
91 for ( i=0 ; i< 6 ; i++ ) {
92 if ( !Q_stricmp( modes[i].name, string ) ) {
93 break;
94 }
95 }
96
97 // hack to prevent trilinear from being set on voodoo,
98 // because their driver freaks...
99 if ( i == 5 && glConfig.hardwareType == GLHW_3DFX_2D3D ) {
100 ri.Printf( PRINT_ALL, "Refusing to set trilinear on a voodoo.\n" );
101 i = 3;
102 }
103
104
105 if ( i == 6 ) {
106 ri.Printf (PRINT_ALL, "bad filter name\n");
107 return;
108 }
109
110 gl_filter_min = modes[i].minimize;
111 gl_filter_max = modes[i].maximize;
112
113 // change all the existing mipmap texture objects
114 for ( i = 0 ; i < tr.numImages ; i++ ) {
115 glt = tr.images[ i ];
116 if ( glt->mipmap ) {
117 GL_Bind (glt);
118 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
119 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
120 }
121 }
122 }
123
124 /*
125 ===============
126 R_SumOfUsedImages
127 ===============
128 */
R_SumOfUsedImages(void)129 int R_SumOfUsedImages( void ) {
130 int total;
131 int i;
132
133 total = 0;
134 for ( i = 0; i < tr.numImages; i++ ) {
135 if ( tr.images[i]->frameUsed == tr.frameCount ) {
136 total += tr.images[i]->uploadWidth * tr.images[i]->uploadHeight;
137 }
138 }
139
140 return total;
141 }
142
143 /*
144 ===============
145 R_ImageList_f
146 ===============
147 */
R_ImageList_f(void)148 void R_ImageList_f( void ) {
149 int i;
150 image_t *image;
151 int texels;
152 const char *yesno[] = {
153 "no ", "yes"
154 };
155
156 ri.Printf (PRINT_ALL, "\n -w-- -h-- -mm- -TMU- -if-- wrap --name-------\n");
157 texels = 0;
158
159 for ( i = 0 ; i < tr.numImages ; i++ ) {
160 image = tr.images[ i ];
161
162 texels += image->uploadWidth*image->uploadHeight;
163 ri.Printf (PRINT_ALL, "%4i: %4i %4i %s %d ",
164 i, image->uploadWidth, image->uploadHeight, yesno[image->mipmap], image->TMU );
165 switch ( image->internalFormat ) {
166 case 1:
167 ri.Printf( PRINT_ALL, "I " );
168 break;
169 case 2:
170 ri.Printf( PRINT_ALL, "IA " );
171 break;
172 case 3:
173 ri.Printf( PRINT_ALL, "RGB " );
174 break;
175 case 4:
176 ri.Printf( PRINT_ALL, "RGBA " );
177 break;
178 case GL_RGBA8:
179 ri.Printf( PRINT_ALL, "RGBA8" );
180 break;
181 case GL_RGB8:
182 ri.Printf( PRINT_ALL, "RGB8" );
183 break;
184 case GL_RGB4_S3TC:
185 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
186 ri.Printf( PRINT_ALL, "S3TC " );
187 break;
188 case GL_RGBA4:
189 ri.Printf( PRINT_ALL, "RGBA4" );
190 break;
191 case GL_RGB5:
192 ri.Printf( PRINT_ALL, "RGB5 " );
193 break;
194 default:
195 ri.Printf( PRINT_ALL, "???? " );
196 }
197
198 switch ( image->wrapClampMode ) {
199 case GL_REPEAT:
200 ri.Printf( PRINT_ALL, "rept " );
201 break;
202 case GL_CLAMP_TO_EDGE:
203 ri.Printf( PRINT_ALL, "clmp " );
204 break;
205 default:
206 ri.Printf( PRINT_ALL, "%4i ", image->wrapClampMode );
207 break;
208 }
209
210 ri.Printf( PRINT_ALL, " %s\n", image->imgName );
211 }
212 ri.Printf (PRINT_ALL, " ---------\n");
213 ri.Printf (PRINT_ALL, " %i total texels (not including mipmaps)\n", texels);
214 ri.Printf (PRINT_ALL, " %i total images\n\n", tr.numImages );
215 }
216
217 //=======================================================================
218
219 /*
220 ================
221 ResampleTexture
222
223 Used to resample images in a more general than quartering fashion.
224
225 This will only be filtered properly if the resampled size
226 is greater than half the original size.
227
228 If a larger shrinking is needed, use the mipmap function
229 before or after.
230 ================
231 */
ResampleTexture(unsigned * in,int inwidth,int inheight,unsigned * out,int outwidth,int outheight)232 static void ResampleTexture( unsigned *in, int inwidth, int inheight, unsigned *out,
233 int outwidth, int outheight ) {
234 int i, j;
235 unsigned *inrow, *inrow2;
236 unsigned frac, fracstep;
237 unsigned p1[2048], p2[2048];
238 byte *pix1, *pix2, *pix3, *pix4;
239
240 if (outwidth>2048)
241 ri.Error(ERR_DROP, "ResampleTexture: max width");
242
243 fracstep = inwidth*0x10000/outwidth;
244
245 frac = fracstep>>2;
246 for ( i=0 ; i<outwidth ; i++ ) {
247 p1[i] = 4*(frac>>16);
248 frac += fracstep;
249 }
250 frac = 3*(fracstep>>2);
251 for ( i=0 ; i<outwidth ; i++ ) {
252 p2[i] = 4*(frac>>16);
253 frac += fracstep;
254 }
255
256 for (i=0 ; i<outheight ; i++, out += outwidth) {
257 inrow = in + inwidth*(int)((i+0.25)*inheight/outheight);
258 inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight);
259 frac = fracstep >> 1;
260 for (j=0 ; j<outwidth ; j++) {
261 pix1 = (byte *)inrow + p1[j];
262 pix2 = (byte *)inrow + p2[j];
263 pix3 = (byte *)inrow2 + p1[j];
264 pix4 = (byte *)inrow2 + p2[j];
265 ((byte *)(out+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
266 ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
267 ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
268 ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
269 }
270 }
271 }
272
273 /*
274 ================
275 R_LightScaleTexture
276
277 Scale up the pixel values in a texture to increase the
278 lighting range
279 ================
280 */
R_LightScaleTexture(unsigned * in,int inwidth,int inheight,qboolean only_gamma)281 void R_LightScaleTexture (unsigned *in, int inwidth, int inheight, qboolean only_gamma )
282 {
283 if ( only_gamma )
284 {
285 if ( !glConfig.deviceSupportsGamma )
286 {
287 int i, c;
288 byte *p;
289
290 p = (byte *)in;
291
292 c = inwidth*inheight;
293 for (i=0 ; i<c ; i++, p+=4)
294 {
295 p[0] = s_gammatable[p[0]];
296 p[1] = s_gammatable[p[1]];
297 p[2] = s_gammatable[p[2]];
298 }
299 }
300 }
301 else
302 {
303 int i, c;
304 byte *p;
305
306 p = (byte *)in;
307
308 c = inwidth*inheight;
309
310 if ( glConfig.deviceSupportsGamma )
311 {
312 for (i=0 ; i<c ; i++, p+=4)
313 {
314 p[0] = s_intensitytable[p[0]];
315 p[1] = s_intensitytable[p[1]];
316 p[2] = s_intensitytable[p[2]];
317 }
318 }
319 else
320 {
321 for (i=0 ; i<c ; i++, p+=4)
322 {
323 p[0] = s_gammatable[s_intensitytable[p[0]]];
324 p[1] = s_gammatable[s_intensitytable[p[1]]];
325 p[2] = s_gammatable[s_intensitytable[p[2]]];
326 }
327 }
328 }
329 }
330
331
332 /*
333 ================
334 R_MipMap2
335
336 Operates in place, quartering the size of the texture
337 Proper linear filter
338 ================
339 */
R_MipMap2(unsigned * in,int inWidth,int inHeight)340 static void R_MipMap2( unsigned *in, int inWidth, int inHeight ) {
341 int i, j, k;
342 byte *outpix;
343 int inWidthMask, inHeightMask;
344 int total;
345 int outWidth, outHeight;
346 unsigned *temp;
347
348 outWidth = inWidth >> 1;
349 outHeight = inHeight >> 1;
350 temp = ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 );
351
352 inWidthMask = inWidth - 1;
353 inHeightMask = inHeight - 1;
354
355 for ( i = 0 ; i < outHeight ; i++ ) {
356 for ( j = 0 ; j < outWidth ; j++ ) {
357 outpix = (byte *) ( temp + i * outWidth + j );
358 for ( k = 0 ; k < 4 ; k++ ) {
359 total =
360 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] +
361 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] +
362 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] +
363 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] +
364
365 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] +
366 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] +
367 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] +
368 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] +
369
370 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] +
371 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] +
372 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] +
373 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] +
374
375 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] +
376 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] +
377 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] +
378 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k];
379 outpix[k] = total / 36;
380 }
381 }
382 }
383
384 Com_Memcpy( in, temp, outWidth * outHeight * 4 );
385 ri.Hunk_FreeTempMemory( temp );
386 }
387
388 /*
389 ================
390 R_MipMap
391
392 Operates in place, quartering the size of the texture
393 ================
394 */
R_MipMap(byte * in,int width,int height)395 static void R_MipMap (byte *in, int width, int height) {
396 int i, j;
397 byte *out;
398 int row;
399
400 if ( !r_simpleMipMaps->integer ) {
401 R_MipMap2( (unsigned *)in, width, height );
402 return;
403 }
404
405 if ( width == 1 && height == 1 ) {
406 return;
407 }
408
409 row = width * 4;
410 out = in;
411 width >>= 1;
412 height >>= 1;
413
414 if ( width == 0 || height == 0 ) {
415 width += height; // get largest
416 for (i=0 ; i<width ; i++, out+=4, in+=8 ) {
417 out[0] = ( in[0] + in[4] )>>1;
418 out[1] = ( in[1] + in[5] )>>1;
419 out[2] = ( in[2] + in[6] )>>1;
420 out[3] = ( in[3] + in[7] )>>1;
421 }
422 return;
423 }
424
425 for (i=0 ; i<height ; i++, in+=row) {
426 for (j=0 ; j<width ; j++, out+=4, in+=8) {
427 out[0] = (in[0] + in[4] + in[row+0] + in[row+4])>>2;
428 out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2;
429 out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2;
430 out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2;
431 }
432 }
433 }
434
435
436 /*
437 ==================
438 R_BlendOverTexture
439
440 Apply a color blend over a set of pixels
441 ==================
442 */
R_BlendOverTexture(byte * data,int pixelCount,byte blend[4])443 static void R_BlendOverTexture( byte *data, int pixelCount, byte blend[4] ) {
444 int i;
445 int inverseAlpha;
446 int premult[3];
447
448 inverseAlpha = 255 - blend[3];
449 premult[0] = blend[0] * blend[3];
450 premult[1] = blend[1] * blend[3];
451 premult[2] = blend[2] * blend[3];
452
453 for ( i = 0 ; i < pixelCount ; i++, data+=4 ) {
454 data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9;
455 data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9;
456 data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9;
457 }
458 }
459
460 byte mipBlendColors[16][4] = {
461 {0,0,0,0},
462 {255,0,0,128},
463 {0,255,0,128},
464 {0,0,255,128},
465 {255,0,0,128},
466 {0,255,0,128},
467 {0,0,255,128},
468 {255,0,0,128},
469 {0,255,0,128},
470 {0,0,255,128},
471 {255,0,0,128},
472 {0,255,0,128},
473 {0,0,255,128},
474 {255,0,0,128},
475 {0,255,0,128},
476 {0,0,255,128},
477 };
478
479
480 /*
481 ===============
482 Upload32
483
484 ===============
485 */
486 extern qboolean charSet;
Upload32(unsigned * data,int width,int height,qboolean mipmap,qboolean picmip,qboolean lightMap,int * format,int * pUploadWidth,int * pUploadHeight)487 static void Upload32( unsigned *data,
488 int width, int height,
489 qboolean mipmap,
490 qboolean picmip,
491 qboolean lightMap,
492 int *format,
493 int *pUploadWidth, int *pUploadHeight )
494 {
495 int samples;
496 unsigned *scaledBuffer = NULL;
497 unsigned *resampledBuffer = NULL;
498 int scaled_width, scaled_height;
499 int i, c;
500 byte *scan;
501 GLenum internalFormat = GL_RGB;
502 float rMax = 0, gMax = 0, bMax = 0;
503
504 //
505 // convert to exact power of 2 sizes
506 //
507 for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
508 ;
509 for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
510 ;
511 if ( r_roundImagesDown->integer && scaled_width > width )
512 scaled_width >>= 1;
513 if ( r_roundImagesDown->integer && scaled_height > height )
514 scaled_height >>= 1;
515
516 if ( scaled_width != width || scaled_height != height ) {
517 resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 );
518 ResampleTexture (data, width, height, resampledBuffer, scaled_width, scaled_height);
519 data = resampledBuffer;
520 width = scaled_width;
521 height = scaled_height;
522 }
523
524 //
525 // perform optional picmip operation
526 //
527 if ( picmip ) {
528 scaled_width >>= r_picmip->integer;
529 scaled_height >>= r_picmip->integer;
530 }
531
532 //
533 // clamp to minimum size
534 //
535 if (scaled_width < 1) {
536 scaled_width = 1;
537 }
538 if (scaled_height < 1) {
539 scaled_height = 1;
540 }
541
542 //
543 // clamp to the current upper OpenGL limit
544 // scale both axis down equally so we don't have to
545 // deal with a half mip resampling
546 //
547 while ( scaled_width > glConfig.maxTextureSize
548 || scaled_height > glConfig.maxTextureSize ) {
549 scaled_width >>= 1;
550 scaled_height >>= 1;
551 }
552
553 scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height );
554
555 //
556 // scan the texture for each channel's max values
557 // and verify if the alpha channel is being used or not
558 //
559 c = width*height;
560 scan = ((byte *)data);
561 samples = 3;
562
563 if(lightMap)
564 {
565 if(r_greyscale->integer)
566 internalFormat = GL_LUMINANCE;
567 else
568 internalFormat = GL_RGB;
569 }
570 else
571 {
572 for ( i = 0; i < c; i++ )
573 {
574 if ( scan[i*4+0] > rMax )
575 {
576 rMax = scan[i*4+0];
577 }
578 if ( scan[i*4+1] > gMax )
579 {
580 gMax = scan[i*4+1];
581 }
582 if ( scan[i*4+2] > bMax )
583 {
584 bMax = scan[i*4+2];
585 }
586 if ( scan[i*4 + 3] != 255 )
587 {
588 samples = 4;
589 break;
590 }
591 }
592 // select proper internal format
593 if ( samples == 3 )
594 {
595 if(r_greyscale->integer)
596 {
597 if(r_texturebits->integer == 16)
598 internalFormat = GL_LUMINANCE8;
599 else if(r_texturebits->integer == 32)
600 internalFormat = GL_LUMINANCE16;
601 else
602 internalFormat = GL_LUMINANCE;
603 }
604 else
605 {
606 if ( glConfig.textureCompression == TC_S3TC_ARB )
607 {
608 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
609 }
610 else if ( glConfig.textureCompression == TC_S3TC )
611 {
612 internalFormat = GL_RGB4_S3TC;
613 }
614 else if ( r_texturebits->integer == 16 )
615 {
616 internalFormat = GL_RGB5;
617 }
618 else if ( r_texturebits->integer == 32 )
619 {
620 internalFormat = GL_RGB8;
621 }
622 else
623 {
624 internalFormat = GL_RGB;
625 }
626 }
627 }
628 else if ( samples == 4 )
629 {
630 if(r_greyscale->integer)
631 {
632 if(r_texturebits->integer == 16)
633 internalFormat = GL_LUMINANCE8_ALPHA8;
634 else if(r_texturebits->integer == 32)
635 internalFormat = GL_LUMINANCE16_ALPHA16;
636 else
637 internalFormat = GL_LUMINANCE_ALPHA;
638 }
639 else
640 {
641 if ( r_texturebits->integer == 16 )
642 {
643 internalFormat = GL_RGBA4;
644 }
645 else if ( r_texturebits->integer == 32 )
646 {
647 internalFormat = GL_RGBA8;
648 }
649 else
650 {
651 internalFormat = GL_RGBA;
652 }
653 }
654 }
655 }
656
657 // copy or resample data as appropriate for first MIP level
658 if ( ( scaled_width == width ) &&
659 ( scaled_height == height ) ) {
660 if (!mipmap)
661 {
662 qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
663 *pUploadWidth = scaled_width;
664 *pUploadHeight = scaled_height;
665 *format = internalFormat;
666
667 goto done;
668 }
669 Com_Memcpy (scaledBuffer, data, width*height*4);
670 }
671 else
672 {
673 // use the normal mip-mapping function to go down from here
674 while ( width > scaled_width || height > scaled_height ) {
675 R_MipMap( (byte *)data, width, height );
676 width >>= 1;
677 height >>= 1;
678 if ( width < 1 ) {
679 width = 1;
680 }
681 if ( height < 1 ) {
682 height = 1;
683 }
684 }
685 Com_Memcpy( scaledBuffer, data, width * height * 4 );
686 }
687
688 R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !mipmap );
689
690 *pUploadWidth = scaled_width;
691 *pUploadHeight = scaled_height;
692 *format = internalFormat;
693
694 qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
695
696 if (mipmap)
697 {
698 int miplevel;
699
700 miplevel = 0;
701 while (scaled_width > 1 || scaled_height > 1)
702 {
703 R_MipMap( (byte *)scaledBuffer, scaled_width, scaled_height );
704 scaled_width >>= 1;
705 scaled_height >>= 1;
706 if (scaled_width < 1)
707 scaled_width = 1;
708 if (scaled_height < 1)
709 scaled_height = 1;
710 miplevel++;
711
712 if ( r_colorMipLevels->integer ) {
713 R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] );
714 }
715
716 qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
717 }
718 }
719 done:
720
721 if (mipmap)
722 {
723 if ( textureFilterAnisotropic )
724 qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
725 (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) );
726
727 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
728 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
729 }
730 else
731 {
732 if ( textureFilterAnisotropic )
733 qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
734
735 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
736 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
737 }
738
739 GL_CheckErrors();
740
741 if ( scaledBuffer != 0 )
742 ri.Hunk_FreeTempMemory( scaledBuffer );
743 if ( resampledBuffer != 0 )
744 ri.Hunk_FreeTempMemory( resampledBuffer );
745 }
746
747
748 /*
749 ================
750 R_CreateImage
751
752 This is the only way any image_t are created
753 ================
754 */
R_CreateImage(const char * name,const byte * pic,int width,int height,qboolean mipmap,qboolean allowPicmip,int glWrapClampMode)755 image_t *R_CreateImage( const char *name, const byte *pic, int width, int height,
756 qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) {
757 image_t *image;
758 qboolean isLightmap = qfalse;
759 long hash;
760
761 if (strlen(name) >= MAX_QPATH ) {
762 ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long\n", name);
763 }
764 if ( !strncmp( name, "*lightmap", 9 ) ) {
765 isLightmap = qtrue;
766 }
767
768 if ( tr.numImages == MAX_DRAWIMAGES ) {
769 ri.Error( ERR_DROP, "R_CreateImage: MAX_DRAWIMAGES hit\n");
770 }
771
772 image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low );
773 image->texnum = 1024 + tr.numImages;
774 tr.numImages++;
775
776 image->mipmap = mipmap;
777 image->allowPicmip = allowPicmip;
778
779 strcpy (image->imgName, name);
780
781 image->width = width;
782 image->height = height;
783 image->wrapClampMode = glWrapClampMode;
784
785 // lightmaps are always allocated on TMU 1
786 if ( qglActiveTextureARB && isLightmap ) {
787 image->TMU = 1;
788 } else {
789 image->TMU = 0;
790 }
791
792 if ( qglActiveTextureARB ) {
793 GL_SelectTexture( image->TMU );
794 }
795
796 GL_Bind(image);
797
798 Upload32( (unsigned *)pic, image->width, image->height,
799 image->mipmap,
800 allowPicmip,
801 isLightmap,
802 &image->internalFormat,
803 &image->uploadWidth,
804 &image->uploadHeight );
805
806 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode );
807 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode );
808
809 qglBindTexture( GL_TEXTURE_2D, 0 );
810
811 if ( image->TMU == 1 ) {
812 GL_SelectTexture( 0 );
813 }
814
815 hash = generateHashValue(name);
816 image->next = hashTable[hash];
817 hashTable[hash] = image;
818
819 return image;
820 }
821
822 //===================================================================
823
824 typedef struct
825 {
826 char *ext;
827 void (*ImageLoader)( const char *, unsigned char **, int *, int * );
828 } imageExtToLoaderMap_t;
829
830 // Note that the ordering indicates the order of preference used
831 // when there are multiple images of different formats available
832 static imageExtToLoaderMap_t imageLoaders[ ] =
833 {
834 { "tga", R_LoadTGA },
835 { "jpg", R_LoadJPG },
836 { "jpeg", R_LoadJPG },
837 { "png", R_LoadPNG },
838 { "pcx", R_LoadPCX },
839 { "bmp", R_LoadBMP }
840 };
841
842 static int numImageLoaders = sizeof( imageLoaders ) /
843 sizeof( imageLoaders[ 0 ] );
844
845 /*
846 =================
847 R_LoadImage
848
849 Loads any of the supported image types into a cannonical
850 32 bit format.
851 =================
852 */
R_LoadImage(const char * name,byte ** pic,int * width,int * height)853 void R_LoadImage( const char *name, byte **pic, int *width, int *height )
854 {
855 qboolean orgNameFailed = qfalse;
856 int i;
857 char localName[ MAX_QPATH ];
858 const char *ext;
859
860 *pic = NULL;
861 *width = 0;
862 *height = 0;
863
864 Q_strncpyz( localName, name, MAX_QPATH );
865
866 ext = COM_GetExtension( localName );
867
868 if( *ext )
869 {
870 // Look for the correct loader and use it
871 for( i = 0; i < numImageLoaders; i++ )
872 {
873 if( !Q_stricmp( ext, imageLoaders[ i ].ext ) )
874 {
875 // Load
876 imageLoaders[ i ].ImageLoader( localName, pic, width, height );
877 break;
878 }
879 }
880
881 // A loader was found
882 if( i < numImageLoaders )
883 {
884 if( *pic == NULL )
885 {
886 // Loader failed, most likely because the file isn't there;
887 // try again without the extension
888 orgNameFailed = qtrue;
889 COM_StripExtension( name, localName, MAX_QPATH );
890 }
891 else
892 {
893 // Something loaded
894 return;
895 }
896 }
897 }
898
899 // Try and find a suitable match using all
900 // the image formats supported
901 for( i = 0; i < numImageLoaders; i++ )
902 {
903 char *altName = va( "%s.%s", localName, imageLoaders[ i ].ext );
904
905 // Load
906 imageLoaders[ i ].ImageLoader( altName, pic, width, height );
907
908 if( *pic )
909 {
910 if( orgNameFailed )
911 {
912 ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n",
913 name, altName );
914 }
915
916 break;
917 }
918 }
919 }
920
921
922 /*
923 ===============
924 R_FindImageFile
925
926 Finds or loads the given image.
927 Returns NULL if it fails, not a default image.
928 ==============
929 */
R_FindImageFile(const char * name,qboolean mipmap,qboolean allowPicmip,int glWrapClampMode)930 image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) {
931 image_t *image;
932 int width, height;
933 byte *pic;
934 long hash;
935
936 if (!name) {
937 return NULL;
938 }
939
940 hash = generateHashValue(name);
941
942 //
943 // see if the image is already loaded
944 //
945 for (image=hashTable[hash]; image; image=image->next) {
946 if ( !strcmp( name, image->imgName ) ) {
947 // the white image can be used with any set of parms, but other mismatches are errors
948 if ( strcmp( name, "*white" ) ) {
949 if ( image->mipmap != mipmap ) {
950 ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed mipmap parm\n", name );
951 }
952 if ( image->allowPicmip != allowPicmip ) {
953 ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed allowPicmip parm\n", name );
954 }
955 if ( image->wrapClampMode != glWrapClampMode ) {
956 ri.Printf( PRINT_ALL, "WARNING: reused image %s with mixed glWrapClampMode parm\n", name );
957 }
958 }
959 return image;
960 }
961 }
962
963 //
964 // load the pic from disk
965 //
966 R_LoadImage( name, &pic, &width, &height );
967 if ( pic == NULL ) {
968 return NULL;
969 }
970
971 image = R_CreateImage( ( char * ) name, pic, width, height, mipmap, allowPicmip, glWrapClampMode );
972 ri.Free( pic );
973 return image;
974 }
975
976
977 /*
978 ================
979 R_CreateDlightImage
980 ================
981 */
982 #define DLIGHT_SIZE 16
R_CreateDlightImage(void)983 static void R_CreateDlightImage( void ) {
984 int x,y;
985 byte data[DLIGHT_SIZE][DLIGHT_SIZE][4];
986 int b;
987
988 // make a centered inverse-square falloff blob for dynamic lighting
989 for (x=0 ; x<DLIGHT_SIZE ; x++) {
990 for (y=0 ; y<DLIGHT_SIZE ; y++) {
991 float d;
992
993 d = ( DLIGHT_SIZE/2 - 0.5f - x ) * ( DLIGHT_SIZE/2 - 0.5f - x ) +
994 ( DLIGHT_SIZE/2 - 0.5f - y ) * ( DLIGHT_SIZE/2 - 0.5f - y );
995 b = 4000 / d;
996 if (b > 255) {
997 b = 255;
998 } else if ( b < 75 ) {
999 b = 0;
1000 }
1001 data[y][x][0] =
1002 data[y][x][1] =
1003 data[y][x][2] = b;
1004 data[y][x][3] = 255;
1005 }
1006 }
1007 tr.dlightImage = R_CreateImage("*dlight", (byte *)data, DLIGHT_SIZE, DLIGHT_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE );
1008 }
1009
1010
1011 /*
1012 =================
1013 R_InitFogTable
1014 =================
1015 */
R_InitFogTable(void)1016 void R_InitFogTable( void ) {
1017 int i;
1018 float d;
1019 float exp;
1020
1021 exp = 0.5;
1022
1023 for ( i = 0 ; i < FOG_TABLE_SIZE ; i++ ) {
1024 d = pow ( (float)i/(FOG_TABLE_SIZE-1), exp );
1025
1026 tr.fogTable[i] = d;
1027 }
1028 }
1029
1030 /*
1031 ================
1032 R_FogFactor
1033
1034 Returns a 0.0 to 1.0 fog density value
1035 This is called for each texel of the fog texture on startup
1036 and for each vertex of transparent shaders in fog dynamically
1037 ================
1038 */
R_FogFactor(float s,float t)1039 float R_FogFactor( float s, float t ) {
1040 float d;
1041
1042 s -= 1.0/512;
1043 if ( s < 0 ) {
1044 return 0;
1045 }
1046 if ( t < 1.0/32 ) {
1047 return 0;
1048 }
1049 if ( t < 31.0/32 ) {
1050 s *= (t - 1.0f/32.0f) / (30.0f/32.0f);
1051 }
1052
1053 // we need to leave a lot of clamp range
1054 s *= 8;
1055
1056 if ( s > 1.0 ) {
1057 s = 1.0;
1058 }
1059
1060 d = tr.fogTable[ (int)(s * (FOG_TABLE_SIZE-1)) ];
1061
1062 return d;
1063 }
1064
1065 /*
1066 ================
1067 R_CreateFogImage
1068 ================
1069 */
1070 #define FOG_S 256
1071 #define FOG_T 32
R_CreateFogImage(void)1072 static void R_CreateFogImage( void ) {
1073 int x,y;
1074 byte *data;
1075 float g;
1076 float d;
1077 float borderColor[4];
1078
1079 data = ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 );
1080
1081 g = 2.0;
1082
1083 // S is distance, T is depth
1084 for (x=0 ; x<FOG_S ; x++) {
1085 for (y=0 ; y<FOG_T ; y++) {
1086 d = R_FogFactor( ( x + 0.5f ) / FOG_S, ( y + 0.5f ) / FOG_T );
1087
1088 data[(y*FOG_S+x)*4+0] =
1089 data[(y*FOG_S+x)*4+1] =
1090 data[(y*FOG_S+x)*4+2] = 255;
1091 data[(y*FOG_S+x)*4+3] = 255*d;
1092 }
1093 }
1094 // standard openGL clamping doesn't really do what we want -- it includes
1095 // the border color at the edges. OpenGL 1.2 has clamp-to-edge, which does
1096 // what we want.
1097 tr.fogImage = R_CreateImage("*fog", (byte *)data, FOG_S, FOG_T, qfalse, qfalse, GL_CLAMP_TO_EDGE );
1098 ri.Hunk_FreeTempMemory( data );
1099
1100 borderColor[0] = 1.0;
1101 borderColor[1] = 1.0;
1102 borderColor[2] = 1.0;
1103 borderColor[3] = 1;
1104
1105 qglTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor );
1106 }
1107
1108 /*
1109 ==================
1110 R_CreateDefaultImage
1111 ==================
1112 */
1113 #define DEFAULT_SIZE 16
R_CreateDefaultImage(void)1114 static void R_CreateDefaultImage( void ) {
1115 int x;
1116 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
1117
1118 // the default image will be a box, to allow you to see the mapping coordinates
1119 Com_Memset( data, 32, sizeof( data ) );
1120 for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
1121 data[0][x][0] =
1122 data[0][x][1] =
1123 data[0][x][2] =
1124 data[0][x][3] = 255;
1125
1126 data[x][0][0] =
1127 data[x][0][1] =
1128 data[x][0][2] =
1129 data[x][0][3] = 255;
1130
1131 data[DEFAULT_SIZE-1][x][0] =
1132 data[DEFAULT_SIZE-1][x][1] =
1133 data[DEFAULT_SIZE-1][x][2] =
1134 data[DEFAULT_SIZE-1][x][3] = 255;
1135
1136 data[x][DEFAULT_SIZE-1][0] =
1137 data[x][DEFAULT_SIZE-1][1] =
1138 data[x][DEFAULT_SIZE-1][2] =
1139 data[x][DEFAULT_SIZE-1][3] = 255;
1140 }
1141 tr.defaultImage = R_CreateImage("*default", (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, qtrue, qfalse, GL_REPEAT );
1142 }
1143
1144 /*
1145 ==================
1146 R_CreateBuiltinImages
1147 ==================
1148 */
R_CreateBuiltinImages(void)1149 void R_CreateBuiltinImages( void ) {
1150 int x,y;
1151 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
1152
1153 R_CreateDefaultImage();
1154
1155 // we use a solid white image instead of disabling texturing
1156 Com_Memset( data, 255, sizeof( data ) );
1157 tr.whiteImage = R_CreateImage("*white", (byte *)data, 8, 8, qfalse, qfalse, GL_REPEAT );
1158
1159 // with overbright bits active, we need an image which is some fraction of full color,
1160 // for default lightmaps, etc
1161 for (x=0 ; x<DEFAULT_SIZE ; x++) {
1162 for (y=0 ; y<DEFAULT_SIZE ; y++) {
1163 data[y][x][0] =
1164 data[y][x][1] =
1165 data[y][x][2] = tr.identityLightByte;
1166 data[y][x][3] = 255;
1167 }
1168 }
1169
1170 tr.identityLightImage = R_CreateImage("*identityLight", (byte *)data, 8, 8, qfalse, qfalse, GL_REPEAT );
1171
1172
1173 for(x=0;x<32;x++) {
1174 // scratchimage is usually used for cinematic drawing
1175 tr.scratchImage[x] = R_CreateImage("*scratch", (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, qfalse, qtrue, GL_CLAMP_TO_EDGE );
1176 }
1177
1178 R_CreateDlightImage();
1179 R_CreateFogImage();
1180 }
1181
1182
1183 /*
1184 ===============
1185 R_SetColorMappings
1186 ===============
1187 */
R_SetColorMappings(void)1188 void R_SetColorMappings( void ) {
1189 int i, j;
1190 float g;
1191 int inf;
1192 int shift;
1193
1194 // setup the overbright lighting
1195 tr.overbrightBits = r_overBrightBits->integer;
1196 if ( !glConfig.deviceSupportsGamma ) {
1197 tr.overbrightBits = 0; // need hardware gamma for overbright
1198 }
1199
1200 // never overbright in windowed mode
1201 if ( !glConfig.isFullscreen )
1202 {
1203 tr.overbrightBits = 0;
1204 }
1205
1206 // allow 2 overbright bits in 24 bit, but only 1 in 16 bit
1207 if ( glConfig.colorBits > 16 ) {
1208 if ( tr.overbrightBits > 2 ) {
1209 tr.overbrightBits = 2;
1210 }
1211 } else {
1212 if ( tr.overbrightBits > 1 ) {
1213 tr.overbrightBits = 1;
1214 }
1215 }
1216 if ( tr.overbrightBits < 0 ) {
1217 tr.overbrightBits = 0;
1218 }
1219
1220 tr.identityLight = 1.0f / ( 1 << tr.overbrightBits );
1221 tr.identityLightByte = 255 * tr.identityLight;
1222
1223
1224 if ( r_intensity->value <= 1 ) {
1225 ri.Cvar_Set( "r_intensity", "1" );
1226 }
1227
1228 if ( r_gamma->value < 0.5f ) {
1229 ri.Cvar_Set( "r_gamma", "0.5" );
1230 } else if ( r_gamma->value > 3.0f ) {
1231 ri.Cvar_Set( "r_gamma", "3.0" );
1232 }
1233
1234 g = r_gamma->value;
1235
1236 shift = tr.overbrightBits;
1237
1238 for ( i = 0; i < 256; i++ ) {
1239 if ( g == 1 ) {
1240 inf = i;
1241 } else {
1242 inf = 255 * pow ( i/255.0f, 1.0f / g ) + 0.5f;
1243 }
1244 inf <<= shift;
1245 if (inf < 0) {
1246 inf = 0;
1247 }
1248 if (inf > 255) {
1249 inf = 255;
1250 }
1251 s_gammatable[i] = inf;
1252 }
1253
1254 for (i=0 ; i<256 ; i++) {
1255 j = i * r_intensity->value;
1256 if (j > 255) {
1257 j = 255;
1258 }
1259 s_intensitytable[i] = j;
1260 }
1261
1262 if ( glConfig.deviceSupportsGamma )
1263 {
1264 GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable );
1265 }
1266 }
1267
1268 /*
1269 ===============
1270 R_InitImages
1271 ===============
1272 */
R_InitImages(void)1273 void R_InitImages( void ) {
1274 Com_Memset(hashTable, 0, sizeof(hashTable));
1275 // build brightness translation tables
1276 R_SetColorMappings();
1277
1278 // create default texture and white texture
1279 R_CreateBuiltinImages();
1280 }
1281
1282 /*
1283 ===============
1284 R_DeleteTextures
1285 ===============
1286 */
R_DeleteTextures(void)1287 void R_DeleteTextures( void ) {
1288 int i;
1289
1290 for ( i=0; i<tr.numImages ; i++ ) {
1291 qglDeleteTextures( 1, &tr.images[i]->texnum );
1292 }
1293 Com_Memset( tr.images, 0, sizeof( tr.images ) );
1294
1295 tr.numImages = 0;
1296
1297 Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) );
1298 if ( qglActiveTextureARB ) {
1299 GL_SelectTexture( 1 );
1300 qglBindTexture( GL_TEXTURE_2D, 0 );
1301 GL_SelectTexture( 0 );
1302 qglBindTexture( GL_TEXTURE_2D, 0 );
1303 } else {
1304 qglBindTexture( GL_TEXTURE_2D, 0 );
1305 }
1306 }
1307
1308 /*
1309 ============================================================================
1310
1311 SKINS
1312
1313 ============================================================================
1314 */
1315
1316 /*
1317 ==================
1318 CommaParse
1319
1320 This is unfortunate, but the skin files aren't
1321 compatable with our normal parsing rules.
1322 ==================
1323 */
CommaParse(char ** data_p)1324 static char *CommaParse( char **data_p ) {
1325 int c = 0, len;
1326 char *data;
1327 static char com_token[MAX_TOKEN_CHARS];
1328
1329 data = *data_p;
1330 len = 0;
1331 com_token[0] = 0;
1332
1333 // make sure incoming data is valid
1334 if ( !data ) {
1335 *data_p = NULL;
1336 return com_token;
1337 }
1338
1339 while ( 1 ) {
1340 // skip whitespace
1341 while( (c = *data) <= ' ') {
1342 if( !c ) {
1343 break;
1344 }
1345 data++;
1346 }
1347
1348
1349 c = *data;
1350
1351 // skip double slash comments
1352 if ( c == '/' && data[1] == '/' )
1353 {
1354 while (*data && *data != '\n')
1355 data++;
1356 }
1357 // skip /* */ comments
1358 else if ( c=='/' && data[1] == '*' )
1359 {
1360 while ( *data && ( *data != '*' || data[1] != '/' ) )
1361 {
1362 data++;
1363 }
1364 if ( *data )
1365 {
1366 data += 2;
1367 }
1368 }
1369 else
1370 {
1371 break;
1372 }
1373 }
1374
1375 if ( c == 0 ) {
1376 return "";
1377 }
1378
1379 // handle quoted strings
1380 if (c == '\"')
1381 {
1382 data++;
1383 while (1)
1384 {
1385 c = *data++;
1386 if (c=='\"' || !c)
1387 {
1388 com_token[len] = 0;
1389 *data_p = ( char * ) data;
1390 return com_token;
1391 }
1392 if (len < MAX_TOKEN_CHARS)
1393 {
1394 com_token[len] = c;
1395 len++;
1396 }
1397 }
1398 }
1399
1400 // parse a regular word
1401 do
1402 {
1403 if (len < MAX_TOKEN_CHARS)
1404 {
1405 com_token[len] = c;
1406 len++;
1407 }
1408 data++;
1409 c = *data;
1410 } while (c>32 && c != ',' );
1411
1412 if (len == MAX_TOKEN_CHARS)
1413 {
1414 // Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
1415 len = 0;
1416 }
1417 com_token[len] = 0;
1418
1419 *data_p = ( char * ) data;
1420 return com_token;
1421 }
1422
1423
1424 /*
1425 ===============
1426 RE_RegisterSkin
1427
1428 ===============
1429 */
RE_RegisterSkin(const char * name)1430 qhandle_t RE_RegisterSkin( const char *name ) {
1431 qhandle_t hSkin;
1432 skin_t *skin;
1433 skinSurface_t *surf;
1434 union {
1435 char *c;
1436 void *v;
1437 } text;
1438 char *text_p;
1439 char *token;
1440 char surfName[MAX_QPATH];
1441
1442 if ( !name || !name[0] ) {
1443 Com_Printf( "Empty name passed to RE_RegisterSkin\n" );
1444 return 0;
1445 }
1446
1447 if ( strlen( name ) >= MAX_QPATH ) {
1448 Com_Printf( "Skin name exceeds MAX_QPATH\n" );
1449 return 0;
1450 }
1451
1452
1453 // see if the skin is already loaded
1454 for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) {
1455 skin = tr.skins[hSkin];
1456 if ( !Q_stricmp( skin->name, name ) ) {
1457 if( skin->numSurfaces == 0 ) {
1458 return 0; // default skin
1459 }
1460 return hSkin;
1461 }
1462 }
1463
1464 // allocate a new skin
1465 if ( tr.numSkins == MAX_SKINS ) {
1466 ri.Printf( PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name );
1467 return 0;
1468 }
1469 tr.numSkins++;
1470 skin = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
1471 tr.skins[hSkin] = skin;
1472 Q_strncpyz( skin->name, name, sizeof( skin->name ) );
1473 skin->numSurfaces = 0;
1474
1475 // make sure the render thread is stopped
1476 R_SyncRenderThread();
1477
1478 // If not a .skin file, load as a single shader
1479 if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
1480 skin->numSurfaces = 1;
1481 skin->surfaces[0] = ri.Hunk_Alloc( sizeof(skin->surfaces[0]), h_low );
1482 skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue );
1483 return hSkin;
1484 }
1485
1486 // load and parse the skin file
1487 ri.FS_ReadFile( name, &text.v );
1488 if ( !text.c ) {
1489 return 0;
1490 }
1491
1492 text_p = text.c;
1493 while ( text_p && *text_p ) {
1494 // get surface name
1495 token = CommaParse( &text_p );
1496 Q_strncpyz( surfName, token, sizeof( surfName ) );
1497
1498 if ( !token[0] ) {
1499 break;
1500 }
1501 // lowercase the surface name so skin compares are faster
1502 Q_strlwr( surfName );
1503
1504 if ( *text_p == ',' ) {
1505 text_p++;
1506 }
1507
1508 if ( strstr( token, "tag_" ) ) {
1509 continue;
1510 }
1511
1512 // parse the shader name
1513 token = CommaParse( &text_p );
1514
1515 surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
1516 Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
1517 surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue );
1518 skin->numSurfaces++;
1519 }
1520
1521 ri.FS_FreeFile( text.v );
1522
1523
1524 // never let a skin have 0 shaders
1525 if ( skin->numSurfaces == 0 ) {
1526 return 0; // use default skin
1527 }
1528
1529 return hSkin;
1530 }
1531
1532
1533 /*
1534 ===============
1535 R_InitSkins
1536 ===============
1537 */
R_InitSkins(void)1538 void R_InitSkins( void ) {
1539 skin_t *skin;
1540
1541 tr.numSkins = 1;
1542
1543 // make the default skin have all default shaders
1544 skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
1545 Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
1546 skin->numSurfaces = 1;
1547 skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces ), h_low );
1548 skin->surfaces[0]->shader = tr.defaultShader;
1549 }
1550
1551 /*
1552 ===============
1553 R_GetSkinByHandle
1554 ===============
1555 */
R_GetSkinByHandle(qhandle_t hSkin)1556 skin_t *R_GetSkinByHandle( qhandle_t hSkin ) {
1557 if ( hSkin < 1 || hSkin >= tr.numSkins ) {
1558 return tr.skins[0];
1559 }
1560 return tr.skins[ hSkin ];
1561 }
1562
1563 /*
1564 ===============
1565 R_SkinList_f
1566 ===============
1567 */
R_SkinList_f(void)1568 void R_SkinList_f( void ) {
1569 int i, j;
1570 skin_t *skin;
1571
1572 ri.Printf (PRINT_ALL, "------------------\n");
1573
1574 for ( i = 0 ; i < tr.numSkins ; i++ ) {
1575 skin = tr.skins[i];
1576
1577 ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name );
1578 for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
1579 ri.Printf( PRINT_ALL, " %s = %s\n",
1580 skin->surfaces[j]->name, skin->surfaces[j]->shader->name );
1581 }
1582 }
1583 ri.Printf (PRINT_ALL, "------------------\n");
1584 }
1585
1586