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 GLenum temp_GLformat = GL_RGBA;
503 GLenum temp_GLtype = GL_UNSIGNED_BYTE;
504 float rMax = 0, gMax = 0, bMax = 0;
505
506 //
507 // convert to exact power of 2 sizes
508 //
509 for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
510 ;
511 for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
512 ;
513 if ( r_roundImagesDown->integer && scaled_width > width )
514 scaled_width >>= 1;
515 if ( r_roundImagesDown->integer && scaled_height > height )
516 scaled_height >>= 1;
517
518 if ( scaled_width != width || scaled_height != height ) {
519 resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 );
520 ResampleTexture (data, width, height, resampledBuffer, scaled_width, scaled_height);
521 data = resampledBuffer;
522 width = scaled_width;
523 height = scaled_height;
524 }
525
526 //
527 // perform optional picmip operation
528 //
529 if ( picmip ) {
530 scaled_width >>= r_picmip->integer;
531 scaled_height >>= r_picmip->integer;
532 }
533
534 //
535 // clamp to minimum size
536 //
537 if (scaled_width < 1) {
538 scaled_width = 1;
539 }
540 if (scaled_height < 1) {
541 scaled_height = 1;
542 }
543
544 //
545 // clamp to the current upper OpenGL limit
546 // scale both axis down equally so we don't have to
547 // deal with a half mip resampling
548 //
549 while ( scaled_width > glConfig.maxTextureSize
550 || scaled_height > glConfig.maxTextureSize ) {
551 scaled_width >>= 1;
552 scaled_height >>= 1;
553 }
554
555 scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height );
556
557 //
558 // scan the texture for each channel's max values
559 // and verify if the alpha channel is being used or not
560 //
561 c = width*height;
562 scan = ((byte *)data);
563 samples = 3;
564
565 if( r_greyscale->integer )
566 {
567 for ( i = 0; i < c; i++ )
568 {
569 byte luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]);
570 scan[i*4] = luma;
571 scan[i*4 + 1] = luma;
572 scan[i*4 + 2] = luma;
573 }
574 }
575 else if( r_greyscale->value )
576 {
577 for ( i = 0; i < c; i++ )
578 {
579 float luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]);
580 scan[i*4] = LERP(scan[i*4], luma, r_greyscale->value);
581 scan[i*4 + 1] = LERP(scan[i*4 + 1], luma, r_greyscale->value);
582 scan[i*4 + 2] = LERP(scan[i*4 + 2], luma, r_greyscale->value);
583 }
584 }
585
586 if(lightMap)
587 {
588 if(r_greyscale->integer)
589 internalFormat = GL_LUMINANCE;
590 else if(r_monolightmaps->integer)
591 internalFormat = GL_LUMINANCE;
592 else
593 internalFormat = GL_RGB;
594 }
595 else
596 {
597 for ( i = 0; i < c; i++ )
598 {
599 if ( scan[i*4+0] > rMax )
600 {
601 rMax = scan[i*4+0];
602 }
603 if ( scan[i*4+1] > gMax )
604 {
605 gMax = scan[i*4+1];
606 }
607 if ( scan[i*4+2] > bMax )
608 {
609 bMax = scan[i*4+2];
610 }
611 if ( scan[i*4 + 3] != 255 )
612 {
613 samples = 4;
614 break;
615 }
616 }
617 // select proper internal format
618 if ( samples == 3 )
619 {
620 if(r_greyscale->integer)
621 {
622 if(r_texturebits->integer == 16)
623 internalFormat = GL_LUMINANCE8;
624 else if(r_texturebits->integer == 32)
625 internalFormat = GL_LUMINANCE16;
626 else
627 internalFormat = GL_LUMINANCE;
628 }
629 else
630 {
631 if ( glConfig.textureCompression == TC_S3TC_ARB )
632 {
633 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
634 }
635 else if ( glConfig.textureCompression == TC_S3TC )
636 {
637 internalFormat = GL_RGB4_S3TC;
638 }
639 else if ( r_texturebits->integer == 16 )
640 {
641 internalFormat = GL_RGB5;
642 }
643 else if ( r_texturebits->integer == 15 )
644 {
645 internalFormat = GL_RGB5;
646 }
647 else if ( r_texturebits->integer == 12 )
648 {
649 internalFormat = GL_RGB4;
650 }
651 else if ( r_texturebits->integer == 6 )
652 {
653 internalFormat = GL_R3_G3_B2;
654 }
655 else if ( r_texturebits->integer == 32 )
656 {
657 internalFormat = GL_RGB8;
658 }
659 else
660 {
661 internalFormat = GL_RGB;
662 }
663 }
664 }
665 else if ( samples == 4 )
666 {
667 if(r_greyscale->integer)
668 {
669 if(r_texturebits->integer == 16)
670 internalFormat = GL_LUMINANCE8_ALPHA8;
671 else if(r_texturebits->integer == 32)
672 internalFormat = GL_LUMINANCE16_ALPHA16;
673 else
674 internalFormat = GL_LUMINANCE_ALPHA;
675 }
676 else
677 {
678 /*
679 // leilei - This was commented out, because bloom strictly needs GL_RGBA
680 if ( glConfig.textureCompression == TC_S3TC_ARB )
681 {
682 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; // leilei - this was missing
683 }
684 else */
685 if ( r_texturebits->integer == 16 )
686 {
687 internalFormat = GL_RGBA4;
688 }
689 else if ( r_texturebits->integer == 15 )
690 {
691 internalFormat = GL_RGB5_A1;
692 }
693 else if ( r_texturebits->integer == 12 )
694 {
695 internalFormat = GL_RGBA4;
696 }
697 else if ( r_texturebits->integer == 6 )
698 {
699 internalFormat = GL_RGBA2;
700 }
701 else if ( r_texturebits->integer == 32 )
702 {
703 internalFormat = GL_RGBA8;
704 }
705 else
706 {
707 internalFormat = GL_RGBA;
708 }
709 }
710 }
711 }
712
713 if (depthimage)
714 { mipmap=0; internalFormat = GL_DEPTH_COMPONENT; temp_GLformat=GL_DEPTH_COMPONENT; temp_GLtype=GL_FLOAT; }
715 else
716 { temp_GLformat=GL_RGBA; temp_GLtype=GL_UNSIGNED_BYTE; }
717
718 // copy or resample data as appropriate for first MIP level
719 if ( ( scaled_width == width ) &&
720 ( scaled_height == height ) ) {
721 if (!mipmap)
722 {
723 qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, temp_GLformat, temp_GLtype, data);
724 *pUploadWidth = scaled_width;
725 *pUploadHeight = scaled_height;
726 *format = internalFormat;
727
728 goto done;
729 }
730 Com_Memcpy (scaledBuffer, data, width*height*4);
731 }
732 else
733 {
734 // use the normal mip-mapping function to go down from here
735 while ( width > scaled_width || height > scaled_height ) {
736 R_MipMap( (byte *)data, width, height );
737 width >>= 1;
738 height >>= 1;
739 if ( width < 1 ) {
740 width = 1;
741 }
742 if ( height < 1 ) {
743 height = 1;
744 }
745 }
746 Com_Memcpy( scaledBuffer, data, width * height * 4 );
747 }
748
749 R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !mipmap );
750
751 *pUploadWidth = scaled_width;
752 *pUploadHeight = scaled_height;
753 *format = internalFormat;
754
755 qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, temp_GLformat, temp_GLtype, scaledBuffer );
756
757 if (mipmap)
758 {
759 int miplevel;
760
761 miplevel = 0;
762 while (scaled_width > 1 || scaled_height > 1)
763 {
764 R_MipMap( (byte *)scaledBuffer, scaled_width, scaled_height );
765 scaled_width >>= 1;
766 scaled_height >>= 1;
767 if (scaled_width < 1)
768 scaled_width = 1;
769 if (scaled_height < 1)
770 scaled_height = 1;
771 miplevel++;
772
773 if ( r_colorMipLevels->integer ) {
774 R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] );
775 }
776
777 qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, temp_GLformat, temp_GLtype, scaledBuffer );
778 }
779 }
780 done:
781
782 if (mipmap)
783 {
784 if ( textureFilterAnisotropic )
785 qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
786 (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) );
787
788 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
789 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
790 }
791 else
792 {
793 if ( textureFilterAnisotropic )
794 qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
795
796 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
797 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
798 }
799
800 if (depthimage) {
801 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
802 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
803 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
804 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
805 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
806 glTexParameteri (GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_ALPHA);
807
808 }
809 GL_CheckErrors();
810
811 if ( scaledBuffer != 0 )
812 ri.Hunk_FreeTempMemory( scaledBuffer );
813 if ( resampledBuffer != 0 )
814 ri.Hunk_FreeTempMemory( resampledBuffer );
815 }
816
817
818 /*
819 ================
820 R_CreateImage
821
822 This is the only way any image_t are created
823 ================
824 */
R_CreateImage(const char * name,const byte * pic,int width,int height,qboolean mipmap,qboolean allowPicmip,int glWrapClampMode)825 image_t *R_CreateImage( const char *name, const byte *pic, int width, int height,
826 qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) {
827 image_t *image;
828 qboolean isLightmap = qfalse;
829 long hash;
830
831 if (strlen(name) >= MAX_QPATH ) {
832 ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long\n", name);
833 }
834 if ( !strncmp( name, "*lightmap", 9 ) ) {
835 isLightmap = qtrue;
836 }
837
838 if ( tr.numImages == MAX_DRAWIMAGES ) {
839 ri.Error( ERR_DROP, "R_CreateImage: MAX_DRAWIMAGES hit\n");
840 }
841
842 image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low );
843 image->texnum = 1024 + tr.numImages;
844 tr.numImages++;
845
846 image->mipmap = mipmap;
847 image->allowPicmip = allowPicmip;
848
849 strcpy (image->imgName, name);
850
851 image->width = width;
852 image->height = height;
853 image->wrapClampMode = glWrapClampMode;
854
855 // lightmaps are always allocated on TMU 1
856 if ( qglActiveTextureARB && isLightmap ) {
857 image->TMU = 1;
858 } else {
859 image->TMU = 0;
860 }
861
862 if ( qglActiveTextureARB ) {
863 GL_SelectTexture( image->TMU );
864 }
865
866 GL_Bind(image);
867
868 Upload32( (unsigned *)pic, image->width, image->height,
869 image->mipmap,
870 allowPicmip,
871 isLightmap,
872 &image->internalFormat,
873 &image->uploadWidth,
874 &image->uploadHeight );
875
876 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode );
877 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode );
878
879 qglBindTexture( GL_TEXTURE_2D, 0 );
880
881 if ( image->TMU == 1 ) {
882 GL_SelectTexture( 0 );
883 }
884
885 hash = generateHashValue(name);
886 image->next = hashTable[hash];
887 hashTable[hash] = image;
888
889 return image;
890 }
891
892 //===================================================================
893
894 typedef struct
895 {
896 char *ext;
897 void (*ImageLoader)( const char *, unsigned char **, int *, int * );
898 } imageExtToLoaderMap_t;
899
900 // Note that the ordering indicates the order of preference used
901 // when there are multiple images of different formats available
902 static imageExtToLoaderMap_t imageLoaders[ ] =
903 {
904 { "tga", R_LoadTGA },
905 { "jpg", R_LoadJPG },
906 { "jpeg", R_LoadJPG },
907 { "png", R_LoadPNG },
908 { "pcx", R_LoadPCX },
909 { "bmp", R_LoadBMP }
910 };
911
912 static int numImageLoaders = sizeof( imageLoaders ) /
913 sizeof( imageLoaders[ 0 ] );
914
915 /*
916 =================
917 R_LoadImage
918
919 Loads any of the supported image types into a cannonical
920 32 bit format.
921 =================
922 */
R_LoadImage(const char * name,byte ** pic,int * width,int * height)923 void R_LoadImage( const char *name, byte **pic, int *width, int *height )
924 {
925 qboolean orgNameFailed = qfalse;
926 int i;
927 char localName[ MAX_QPATH ];
928 const char *ext;
929
930 *pic = NULL;
931 *width = 0;
932 *height = 0;
933
934 Q_strncpyz( localName, name, MAX_QPATH );
935
936 ext = COM_GetExtension( localName );
937
938 if( *ext )
939 {
940 // Look for the correct loader and use it
941 for( i = 0; i < numImageLoaders; i++ )
942 {
943 if( !Q_stricmp( ext, imageLoaders[ i ].ext ) )
944 {
945 // Load
946 imageLoaders[ i ].ImageLoader( localName, pic, width, height );
947 break;
948 }
949 }
950
951 // A loader was found
952 if( i < numImageLoaders )
953 {
954 if( *pic == NULL )
955 {
956 // Loader failed, most likely because the file isn't there;
957 // try again without the extension
958 orgNameFailed = qtrue;
959 COM_StripExtension( name, localName, MAX_QPATH );
960 }
961 else
962 {
963 // Something loaded
964 return;
965 }
966 }
967 }
968
969 // Try and find a suitable match using all
970 // the image formats supported
971 for( i = 0; i < numImageLoaders; i++ )
972 {
973 char *altName = va( "%s.%s", localName, imageLoaders[ i ].ext );
974
975 // Load
976 imageLoaders[ i ].ImageLoader( altName, pic, width, height );
977
978 if( *pic )
979 {
980 if( orgNameFailed )
981 {
982 ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n",
983 name, altName );
984 }
985
986 break;
987 }
988 }
989 }
990
991
992 /*
993 ===============
994 R_FindImageFile
995
996 Finds or loads the given image.
997 Returns NULL if it fails, not a default image.
998 ==============
999 */
R_FindImageFile(const char * name,qboolean mipmap,qboolean allowPicmip,int glWrapClampMode)1000 image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) {
1001 image_t *image;
1002 int width, height;
1003 byte *pic;
1004 long hash;
1005
1006 if (!name) {
1007 return NULL;
1008 }
1009
1010 hash = generateHashValue(name);
1011
1012 //
1013 // see if the image is already loaded
1014 //
1015 for (image=hashTable[hash]; image; image=image->next) {
1016 if ( !strcmp( name, image->imgName ) ) {
1017 // the white image can be used with any set of parms, but other mismatches are errors
1018 if ( strcmp( name, "*white" ) ) {
1019 if ( image->mipmap != mipmap ) {
1020 ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed mipmap parm\n", name );
1021 }
1022 if ( image->allowPicmip != allowPicmip ) {
1023 ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed allowPicmip parm\n", name );
1024 }
1025 if ( image->wrapClampMode != glWrapClampMode ) {
1026 ri.Printf( PRINT_ALL, "WARNING: reused image %s with mixed glWrapClampMode parm\n", name );
1027 }
1028 }
1029 return image;
1030 }
1031 }
1032
1033 //
1034 // load the pic from disk
1035 //
1036 R_LoadImage( name, &pic, &width, &height );
1037 if ( pic == NULL ) {
1038 return NULL;
1039 }
1040
1041 image = R_CreateImage( ( char * ) name, pic, width, height, mipmap, allowPicmip, glWrapClampMode );
1042 ri.Free( pic );
1043 return image;
1044 }
1045
1046
1047 /*
1048 ================
1049 R_CreateDlightImage
1050 ================
1051 */
1052 #define DLIGHT_SIZE 16
R_CreateDlightImage(void)1053 static void R_CreateDlightImage( void ) {
1054 int x,y;
1055 byte data[DLIGHT_SIZE][DLIGHT_SIZE][4];
1056 int b;
1057
1058 // make a centered inverse-square falloff blob for dynamic lighting
1059 for (x=0 ; x<DLIGHT_SIZE ; x++) {
1060 for (y=0 ; y<DLIGHT_SIZE ; y++) {
1061 float d;
1062
1063 d = ( DLIGHT_SIZE/2 - 0.5f - x ) * ( DLIGHT_SIZE/2 - 0.5f - x ) +
1064 ( DLIGHT_SIZE/2 - 0.5f - y ) * ( DLIGHT_SIZE/2 - 0.5f - y );
1065 b = 4000 / d;
1066 if (b > 255) {
1067 b = 255;
1068 } else if ( b < 75 ) {
1069 b = 0;
1070 }
1071 data[y][x][0] =
1072 data[y][x][1] =
1073 data[y][x][2] = b;
1074 data[y][x][3] = 255;
1075 }
1076 }
1077 tr.dlightImage = R_CreateImage("*dlight", (byte *)data, DLIGHT_SIZE, DLIGHT_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE );
1078 }
1079
1080
1081 /*
1082 =================
1083 R_InitFogTable
1084 =================
1085 */
R_InitFogTable(void)1086 void R_InitFogTable( void ) {
1087 int i;
1088 float d;
1089 float exp;
1090
1091 exp = 0.5;
1092
1093 for ( i = 0 ; i < FOG_TABLE_SIZE ; i++ ) {
1094 d = pow ( (float)i/(FOG_TABLE_SIZE-1), exp );
1095
1096 tr.fogTable[i] = d;
1097 }
1098 }
1099
1100 /*
1101 ================
1102 R_FogFactor
1103
1104 Returns a 0.0 to 1.0 fog density value
1105 This is called for each texel of the fog texture on startup
1106 and for each vertex of transparent shaders in fog dynamically
1107 ================
1108 */
R_FogFactor(float s,float t)1109 float R_FogFactor( float s, float t ) {
1110 float d;
1111
1112 s -= 1.0/512;
1113 if ( s < 0 ) {
1114 return 0;
1115 }
1116 if ( t < 1.0/32 ) {
1117 return 0;
1118 }
1119 if ( t < 31.0/32 ) {
1120 s *= (t - 1.0f/32.0f) / (30.0f/32.0f);
1121 }
1122
1123 // we need to leave a lot of clamp range
1124 s *= 8;
1125
1126 if ( s > 1.0 ) {
1127 s = 1.0;
1128 }
1129
1130 d = tr.fogTable[ (int)(s * (FOG_TABLE_SIZE-1)) ];
1131
1132 return d;
1133 }
1134
1135 /*
1136 ================
1137 R_CreateFogImage
1138 ================
1139 */
1140 #define FOG_S 256
1141 #define FOG_T 32
R_CreateFogImage(void)1142 static void R_CreateFogImage( void ) {
1143 int x,y;
1144 byte *data;
1145 float g;
1146 float d;
1147 float borderColor[4];
1148
1149 data = ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 );
1150
1151 g = 2.0;
1152
1153 // S is distance, T is depth
1154 for (x=0 ; x<FOG_S ; x++) {
1155 for (y=0 ; y<FOG_T ; y++) {
1156 d = R_FogFactor( ( x + 0.5f ) / FOG_S, ( y + 0.5f ) / FOG_T );
1157
1158 data[(y*FOG_S+x)*4+0] =
1159 data[(y*FOG_S+x)*4+1] =
1160 data[(y*FOG_S+x)*4+2] = 255;
1161 data[(y*FOG_S+x)*4+3] = 255*d;
1162 }
1163 }
1164 // standard openGL clamping doesn't really do what we want -- it includes
1165 // the border color at the edges. OpenGL 1.2 has clamp-to-edge, which does
1166 // what we want.
1167 tr.fogImage = R_CreateImage("*fog", (byte *)data, FOG_S, FOG_T, qfalse, qfalse, GL_CLAMP_TO_EDGE );
1168 ri.Hunk_FreeTempMemory( data );
1169
1170 borderColor[0] = 1.0;
1171 borderColor[1] = 1.0;
1172 borderColor[2] = 1.0;
1173 borderColor[3] = 1;
1174
1175 qglTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor );
1176 }
1177
1178 /*
1179 ==================
1180 R_CreateDefaultImage
1181 ==================
1182 */
1183 #define DEFAULT_SIZE 16
R_CreateDefaultImage(void)1184 static void R_CreateDefaultImage( void ) {
1185 int x;
1186 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
1187
1188 // the default image will be a box, to allow you to see the mapping coordinates
1189 Com_Memset( data, 32, sizeof( data ) );
1190 for ( x = 0 ; x < DEFAULT_SIZE ; x++ ) {
1191 data[0][x][0] =
1192 data[0][x][1] =
1193 data[0][x][2] =
1194 data[0][x][3] = 255;
1195
1196 data[x][0][0] =
1197 data[x][0][1] =
1198 data[x][0][2] =
1199 data[x][0][3] = 255;
1200
1201 data[DEFAULT_SIZE-1][x][0] =
1202 data[DEFAULT_SIZE-1][x][1] =
1203 data[DEFAULT_SIZE-1][x][2] =
1204 data[DEFAULT_SIZE-1][x][3] = 255;
1205
1206 data[x][DEFAULT_SIZE-1][0] =
1207 data[x][DEFAULT_SIZE-1][1] =
1208 data[x][DEFAULT_SIZE-1][2] =
1209 data[x][DEFAULT_SIZE-1][3] = 255;
1210 }
1211 tr.defaultImage = R_CreateImage("*default", (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, qtrue, qfalse, GL_REPEAT );
1212 }
1213
1214 /*
1215 ==================
1216 R_CreateBuiltinImages
1217 ==================
1218 */
R_CreateBuiltinImages(void)1219 void R_CreateBuiltinImages( void ) {
1220 int x,y;
1221 byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
1222
1223 R_CreateDefaultImage();
1224
1225 // we use a solid white image instead of disabling texturing
1226 Com_Memset( data, 255, sizeof( data ) );
1227 tr.whiteImage = R_CreateImage("*white", (byte *)data, 8, 8, qfalse, qfalse, GL_REPEAT );
1228
1229 // with overbright bits active, we need an image which is some fraction of full color,
1230 // for default lightmaps, etc
1231 for (x=0 ; x<DEFAULT_SIZE ; x++) {
1232 for (y=0 ; y<DEFAULT_SIZE ; y++) {
1233 data[y][x][0] =
1234 data[y][x][1] =
1235 data[y][x][2] = tr.identityLightByte;
1236 data[y][x][3] = 255;
1237 }
1238 }
1239
1240 tr.identityLightImage = R_CreateImage("*identityLight", (byte *)data, 8, 8, qfalse, qfalse, GL_REPEAT );
1241
1242
1243 for(x=0;x<32;x++) {
1244 // scratchimage is usually used for cinematic drawing
1245 tr.scratchImage[x] = R_CreateImage("*scratch", (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, qfalse, qtrue, GL_CLAMP_TO_EDGE );
1246 }
1247
1248 R_CreateDlightImage();
1249 R_CreateFogImage();
1250 }
1251
1252
1253 /*
1254 ===============
1255 R_SetColorMappings
1256 ===============
1257 */
R_SetColorMappings(void)1258 void R_SetColorMappings( void ) {
1259 int i, j;
1260 float g;
1261 int inf;
1262 int shift;
1263
1264 // setup the overbright lighting
1265 tr.overbrightBits = r_overBrightBits->integer;
1266 if ( !glConfig.deviceSupportsGamma ) {
1267 tr.overbrightBits = 0; // need hardware gamma for overbright
1268 }
1269
1270 // never overbright in windowed mode
1271 if ( !glConfig.isFullscreen )
1272 {
1273 tr.overbrightBits = 0;
1274 }
1275
1276 // allow 2 overbright bits in 24 bit, but only 1 in 16 bit
1277 if ( glConfig.colorBits > 16 ) {
1278 if ( tr.overbrightBits > 2 ) {
1279 tr.overbrightBits = 2;
1280 }
1281 } else {
1282 if ( tr.overbrightBits > 1 ) {
1283 tr.overbrightBits = 1;
1284 }
1285 }
1286 if ( tr.overbrightBits < 0 ) {
1287 tr.overbrightBits = 0;
1288 }
1289
1290 tr.identityLight = 1.0f / ( 1 << tr.overbrightBits );
1291 tr.identityLightByte = 255 * tr.identityLight;
1292
1293
1294 if ( r_intensity->value <= 1 ) {
1295 ri.Cvar_Set( "r_intensity", "1" );
1296 }
1297
1298 if ( r_gamma->value < 0.5f ) {
1299 ri.Cvar_Set( "r_gamma", "0.5" );
1300 } else if ( r_gamma->value > 3.0f ) {
1301 ri.Cvar_Set( "r_gamma", "3.0" );
1302 }
1303
1304 g = r_gamma->value;
1305
1306 shift = tr.overbrightBits;
1307
1308 for ( i = 0; i < 256; i++ ) {
1309 if ( g == 1 ) {
1310 inf = i;
1311 } else {
1312 inf = 255 * pow ( i/255.0f, 1.0f / g ) + 0.5f;
1313 }
1314 inf <<= shift;
1315 if (inf < 0) {
1316 inf = 0;
1317 }
1318 if (inf > 255) {
1319 inf = 255;
1320 }
1321 s_gammatable[i] = inf;
1322 }
1323
1324 for (i=0 ; i<256 ; i++) {
1325 j = i * r_intensity->value;
1326 if (j > 255) {
1327 j = 255;
1328 }
1329 s_intensitytable[i] = j;
1330 }
1331
1332 if ( glConfig.deviceSupportsGamma )
1333 {
1334 GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable );
1335 }
1336 }
1337
1338 /*
1339 ===============
1340 R_InitImages
1341 ===============
1342 */
R_InitImages(void)1343 void R_InitImages( void ) {
1344 Com_Memset(hashTable, 0, sizeof(hashTable));
1345 // build brightness translation tables
1346 R_SetColorMappings();
1347
1348 // create default texture and white texture
1349 R_CreateBuiltinImages();
1350 }
1351
1352 /*
1353 ===============
1354 R_DeleteTextures
1355 ===============
1356 */
R_DeleteTextures(void)1357 void R_DeleteTextures( void ) {
1358 int i;
1359
1360 for ( i=0; i<tr.numImages ; i++ ) {
1361 qglDeleteTextures( 1, &tr.images[i]->texnum );
1362 }
1363 Com_Memset( tr.images, 0, sizeof( tr.images ) );
1364
1365 tr.numImages = 0;
1366
1367 Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) );
1368 if ( qglActiveTextureARB ) {
1369 GL_SelectTexture( 1 );
1370 qglBindTexture( GL_TEXTURE_2D, 0 );
1371 GL_SelectTexture( 0 );
1372 qglBindTexture( GL_TEXTURE_2D, 0 );
1373 } else {
1374 qglBindTexture( GL_TEXTURE_2D, 0 );
1375 }
1376 }
1377
1378 /*
1379 ============================================================================
1380
1381 SKINS
1382
1383 ============================================================================
1384 */
1385
1386 /*
1387 ==================
1388 CommaParse
1389
1390 This is unfortunate, but the skin files aren't
1391 compatable with our normal parsing rules.
1392 ==================
1393 */
CommaParse(char ** data_p)1394 static char *CommaParse( char **data_p ) {
1395 int c = 0, len;
1396 char *data;
1397 static char com_token[MAX_TOKEN_CHARS];
1398
1399 data = *data_p;
1400 len = 0;
1401 com_token[0] = 0;
1402
1403 // make sure incoming data is valid
1404 if ( !data ) {
1405 *data_p = NULL;
1406 return com_token;
1407 }
1408
1409 while ( 1 ) {
1410 // skip whitespace
1411 while( (c = *data) <= ' ') {
1412 if( !c ) {
1413 break;
1414 }
1415 data++;
1416 }
1417
1418
1419 c = *data;
1420
1421 // skip double slash comments
1422 if ( c == '/' && data[1] == '/' )
1423 {
1424 while (*data && *data != '\n')
1425 data++;
1426 }
1427 // skip /* */ comments
1428 else if ( c=='/' && data[1] == '*' )
1429 {
1430 while ( *data && ( *data != '*' || data[1] != '/' ) )
1431 {
1432 data++;
1433 }
1434 if ( *data )
1435 {
1436 data += 2;
1437 }
1438 }
1439 else
1440 {
1441 break;
1442 }
1443 }
1444
1445 if ( c == 0 ) {
1446 return "";
1447 }
1448
1449 // handle quoted strings
1450 if (c == '\"')
1451 {
1452 data++;
1453 while (1)
1454 {
1455 c = *data++;
1456 if (c=='\"' || !c)
1457 {
1458 com_token[len] = 0;
1459 *data_p = ( char * ) data;
1460 return com_token;
1461 }
1462 if (len < MAX_TOKEN_CHARS)
1463 {
1464 com_token[len] = c;
1465 len++;
1466 }
1467 }
1468 }
1469
1470 // parse a regular word
1471 do
1472 {
1473 if (len < MAX_TOKEN_CHARS)
1474 {
1475 com_token[len] = c;
1476 len++;
1477 }
1478 data++;
1479 c = *data;
1480 } while (c>32 && c != ',' );
1481
1482 if (len == MAX_TOKEN_CHARS)
1483 {
1484 // Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
1485 len = 0;
1486 }
1487 com_token[len] = 0;
1488
1489 *data_p = ( char * ) data;
1490 return com_token;
1491 }
1492
1493
1494 /*
1495 ===============
1496 RE_RegisterSkin
1497
1498 ===============
1499 */
RE_RegisterSkin(const char * name)1500 qhandle_t RE_RegisterSkin( const char *name ) {
1501 qhandle_t hSkin;
1502 skin_t *skin;
1503 skinSurface_t *surf;
1504 union {
1505 char *c;
1506 void *v;
1507 } text;
1508 char *text_p;
1509 char *token;
1510 char surfName[MAX_QPATH];
1511
1512 if ( !name || !name[0] ) {
1513 Com_Printf( "Empty name passed to RE_RegisterSkin\n" );
1514 return 0;
1515 }
1516
1517 if ( strlen( name ) >= MAX_QPATH ) {
1518 Com_Printf( "Skin name exceeds MAX_QPATH\n" );
1519 return 0;
1520 }
1521
1522
1523 // see if the skin is already loaded
1524 for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) {
1525 skin = tr.skins[hSkin];
1526 if ( !Q_stricmp( skin->name, name ) ) {
1527 if( skin->numSurfaces == 0 ) {
1528 return 0; // default skin
1529 }
1530 return hSkin;
1531 }
1532 }
1533
1534 // allocate a new skin
1535 if ( tr.numSkins == MAX_SKINS ) {
1536 ri.Printf( PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name );
1537 return 0;
1538 }
1539 tr.numSkins++;
1540 skin = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
1541 tr.skins[hSkin] = skin;
1542 Q_strncpyz( skin->name, name, sizeof( skin->name ) );
1543 skin->numSurfaces = 0;
1544
1545 // make sure the render thread is stopped
1546 R_SyncRenderThread();
1547
1548 // If not a .skin file, load as a single shader
1549 if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
1550 skin->numSurfaces = 1;
1551 skin->surfaces[0] = ri.Hunk_Alloc( sizeof(skin->surfaces[0]), h_low );
1552 skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue );
1553 return hSkin;
1554 }
1555
1556 // load and parse the skin file
1557 ri.FS_ReadFile( name, &text.v );
1558 if ( !text.c ) {
1559 return 0;
1560 }
1561
1562 text_p = text.c;
1563 while ( text_p && *text_p ) {
1564 // get surface name
1565 token = CommaParse( &text_p );
1566 Q_strncpyz( surfName, token, sizeof( surfName ) );
1567
1568 if ( !token[0] ) {
1569 break;
1570 }
1571 // lowercase the surface name so skin compares are faster
1572 Q_strlwr( surfName );
1573
1574 if ( *text_p == ',' ) {
1575 text_p++;
1576 }
1577
1578 if ( strstr( token, "tag_" ) ) {
1579 continue;
1580 }
1581
1582 // parse the shader name
1583 token = CommaParse( &text_p );
1584
1585 surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
1586 Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
1587 surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue );
1588 skin->numSurfaces++;
1589 }
1590
1591 ri.FS_FreeFile( text.v );
1592
1593
1594 // never let a skin have 0 shaders
1595 if ( skin->numSurfaces == 0 ) {
1596 return 0; // use default skin
1597 }
1598
1599 return hSkin;
1600 }
1601
1602
1603 /*
1604 ===============
1605 R_InitSkins
1606 ===============
1607 */
R_InitSkins(void)1608 void R_InitSkins( void ) {
1609 skin_t *skin;
1610
1611 tr.numSkins = 1;
1612
1613 // make the default skin have all default shaders
1614 skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
1615 Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
1616 skin->numSurfaces = 1;
1617 skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces ), h_low );
1618 skin->surfaces[0]->shader = tr.defaultShader;
1619 }
1620
1621 /*
1622 ===============
1623 R_GetSkinByHandle
1624 ===============
1625 */
R_GetSkinByHandle(qhandle_t hSkin)1626 skin_t *R_GetSkinByHandle( qhandle_t hSkin ) {
1627 if ( hSkin < 1 || hSkin >= tr.numSkins ) {
1628 return tr.skins[0];
1629 }
1630 return tr.skins[ hSkin ];
1631 }
1632
1633 /*
1634 ===============
1635 R_SkinList_f
1636 ===============
1637 */
R_SkinList_f(void)1638 void R_SkinList_f( void ) {
1639 int i, j;
1640 skin_t *skin;
1641
1642 ri.Printf (PRINT_ALL, "------------------\n");
1643
1644 for ( i = 0 ; i < tr.numSkins ; i++ ) {
1645 skin = tr.skins[i];
1646
1647 ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name );
1648 for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
1649 ri.Printf( PRINT_ALL, " %s = %s\n",
1650 skin->surfaces[j]->name, skin->surfaces[j]->shader->name );
1651 }
1652 }
1653 ri.Printf (PRINT_ALL, "------------------\n");
1654 }
1655
1656