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