1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2006 Robert Beckebans <trebor_7@users.sourceforge.net>
5 
6 This file is part of XreaL source code.
7 
8 XreaL source code is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 XreaL source code is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with XreaL source code; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 // tr_shader.c -- this file deals with the parsing and definition of shaders
24 #include "tr_local.h"
25 
26 #define MAX_GUIDETEXT_HASH		2048
27 static char  		  **guideTextHashTable[MAX_GUIDETEXT_HASH];
28 
29 #define MAX_SHADERTABLE_HASH	1024
30 static shaderTable_t   *shaderTableHashTable[MAX_SHADERTABLE_HASH];
31 
32 #define FILE_HASH_SIZE			1024
33 static shader_t		   *shaderHashTable[FILE_HASH_SIZE];
34 
35 #define MAX_SHADERTEXT_HASH		2048
36 static char  		  **shaderTextHashTable[MAX_SHADERTEXT_HASH];
37 
38 static char            *s_guideText;
39 static char            *s_shaderText;
40 
41 // the shader is parsed into these global variables, then copied into
42 // dynamically allocated memory if it is valid.
43 static shaderTable_t	table;
44 static shaderStage_t	stages[MAX_SHADER_STAGES];
45 static shader_t			shader;
46 static texModInfo_t		texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS];
47 static qboolean			deferLoad;
48 
49 /*
50 ================
51 return a hash value for the filename
52 ================
53 */
generateHashValue(const char * fname,const int size)54 static long generateHashValue(const char *fname, const int size)
55 {
56 	int             i;
57 //	int             len;
58 	long            hash;
59 	char            letter;
60 
61 	hash = 0;
62 	i = 0;
63 //	len = strlen(fname);
64 
65 	while(fname[i] != '\0')
66 //	for(i = 0; i < len; i++)
67 	{
68 		letter = tolower(fname[i]);
69 
70 		if(letter == '.')
71 			break;				// don't include extension
72 
73 		if(letter == '\\')
74 			letter = '/';		// damn path names
75 
76 		if(letter == PATH_SEP)
77 			letter = '/';		// damn path names
78 
79 		hash += (long)(letter) * (i + 119);
80 		i++;
81 	}
82 	hash = (hash ^ (hash >> 10) ^ (hash >> 20));
83 	hash &= (size - 1);
84 	return hash;
85 }
86 
R_RemapShader(const char * shaderName,const char * newShaderName,const char * timeOffset)87 void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset)
88 {
89 	char            strippedName[MAX_QPATH];
90 	int             hash;
91 	shader_t       *sh, *sh2;
92 	qhandle_t       h;
93 
94 	sh = R_FindShaderByName(shaderName);
95 	if(sh == NULL || sh == tr.defaultShader)
96 	{
97 		h = RE_RegisterShaderLightMap(shaderName, 0);
98 		sh = R_GetShaderByHandle(h);
99 	}
100 	if(sh == NULL || sh == tr.defaultShader)
101 	{
102 		ri.Printf(PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName);
103 		return;
104 	}
105 
106 	sh2 = R_FindShaderByName(newShaderName);
107 	if(sh2 == NULL || sh2 == tr.defaultShader)
108 	{
109 		h = RE_RegisterShaderLightMap(newShaderName, 0);
110 		sh2 = R_GetShaderByHandle(h);
111 	}
112 
113 	if(sh2 == NULL || sh2 == tr.defaultShader)
114 	{
115 		ri.Printf(PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName);
116 		return;
117 	}
118 
119 	// remap all the shaders with the given name
120 	// even tho they might have different lightmaps
121 	Com_StripExtension(shaderName, strippedName, sizeof(strippedName));
122 	hash = generateHashValue(strippedName, FILE_HASH_SIZE);
123 	for(sh = shaderHashTable[hash]; sh; sh = sh->next)
124 	{
125 		if(Q_stricmp(sh->name, strippedName) == 0)
126 		{
127 			if(sh != sh2)
128 			{
129 				sh->remappedShader = sh2;
130 			}
131 			else
132 			{
133 				sh->remappedShader = NULL;
134 			}
135 		}
136 	}
137 	if(timeOffset)
138 	{
139 		sh2->timeOffset = atof(timeOffset);
140 	}
141 }
142 
143 /*
144 ===============
145 ParseVector
146 ===============
147 */
ParseVector(char ** text,int count,float * v)148 static qboolean ParseVector(char **text, int count, float *v)
149 {
150 	char           *token;
151 	int             i;
152 
153 	token = Com_ParseExt(text, qfalse);
154 	if(strcmp(token, "("))
155 	{
156 		ri.Printf(PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name);
157 		return qfalse;
158 	}
159 
160 	for(i = 0; i < count; i++)
161 	{
162 		token = Com_ParseExt(text, qfalse);
163 		if(!token[0])
164 		{
165 			ri.Printf(PRINT_WARNING, "WARNING: missing vector element in shader '%s'\n", shader.name);
166 			return qfalse;
167 		}
168 		v[i] = atof(token);
169 	}
170 
171 	token = Com_ParseExt(text, qfalse);
172 	if(strcmp(token, ")"))
173 	{
174 		ri.Printf(PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name);
175 		return qfalse;
176 	}
177 
178 	return qtrue;
179 }
180 
181 
182 opstring_t opStrings[] =
183 {
184 	{"bad", OP_BAD},
185 
186 	{"&&", OP_LAND},
187 	{"||", OP_LOR},
188 	{">=", OP_GE},
189 	{"<=", OP_LE},
190 	{"==", OP_LEQ},
191 	{"!=", OP_LNE},
192 
193 	{"+", OP_ADD},
194 	{"-", OP_SUB},
195 	{"/", OP_DIV},
196 	{"%", OP_MOD},
197 	{"*", OP_MUL},
198 	{"neg", OP_NEG},
199 
200 	{"<", OP_LT},
201 	{">", OP_GT},
202 
203 	{"(", OP_LPAREN},
204 	{")", OP_RPAREN},
205 	{"[", OP_LBRACKET},
206 	{"]", OP_RBRACKET},
207 
208 	{"c", OP_NUM},
209 	{"time", OP_TIME},
210 	{"parm0", OP_PARM0},
211 	{"parm1", OP_PARM1},
212 	{"parm2", OP_PARM2},
213 	{"parm3", OP_PARM3},
214 	{"parm4", OP_PARM4},
215 	{"parm5", OP_PARM5},
216 	{"parm6", OP_PARM6},
217 	{"parm7", OP_PARM7},
218 	{"parm8", OP_PARM8},
219 	{"parm9", OP_PARM9},
220 	{"parm10", OP_PARM10},
221 	{"parm11", OP_PARM11},
222 	{"global0", OP_GLOBAL0},
223 	{"global1", OP_GLOBAL1},
224 	{"global2", OP_GLOBAL2},
225 	{"global3", OP_GLOBAL3},
226 	{"global4", OP_GLOBAL4},
227 	{"global5", OP_GLOBAL5},
228 	{"global6", OP_GLOBAL6},
229 	{"global7", OP_GLOBAL7},
230 	{"fragmentPrograms", OP_FRAGMENTPROGRAMS},
231 	{"sound", OP_SOUND},
232 
233 	{"table", OP_TABLE},
234 
235 	{NULL, OP_BAD}
236 };
237 
GetOpType(char * token,expOperation_t * op)238 static void GetOpType(char *token, expOperation_t * op)
239 {
240 	opstring_t     *opString;
241 	char            tableName[MAX_QPATH];
242 	int             hash;
243 	shaderTable_t  *tb;
244 
245 	if(	(token[0] >= '0' && token[0] <= '9')	||
246 		//(token[0] == '-' && token[1] >= '0' && token[1] <= '9')	||
247 		//(token[0] == '+' && token[1] >= '0' && token[1] <= '9')	||
248 		(token[0] == '.' && token[1] >= '0' && token[1] <= '9'))
249 	{
250 		op->type = OP_NUM;
251 		return;
252 	}
253 
254 	Q_strncpyz(tableName, token, sizeof(tableName));
255 	hash = generateHashValue(tableName, MAX_SHADERTABLE_HASH);
256 
257 	for(tb = shaderTableHashTable[hash]; tb; tb = tb->next)
258 	{
259 		if(Q_stricmp(tb->name, tableName) == 0)
260 		{
261 			// match found
262 			op->type = OP_TABLE;
263 			op->value = tb->index;
264 			return;
265 		}
266 	}
267 
268 	for(opString = opStrings; opString->s; opString++)
269 	{
270 		if(!Q_stricmp(token, opString->s))
271 		{
272 			op->type = opString->type;
273 			return;
274 		}
275 	}
276 
277 	op->type = OP_BAD;
278 }
279 
IsOperand(opcode_t oc)280 static qboolean IsOperand(opcode_t oc)
281 {
282 	switch (oc)
283 	{
284 		case OP_NUM:
285 		case OP_TIME:
286 		case OP_PARM0:
287 		case OP_PARM1:
288 		case OP_PARM2:
289 		case OP_PARM3:
290 		case OP_PARM4:
291 		case OP_PARM5:
292 		case OP_PARM6:
293 		case OP_PARM7:
294 		case OP_PARM8:
295 		case OP_PARM9:
296 		case OP_PARM10:
297 		case OP_PARM11:
298 		case OP_GLOBAL0:
299 		case OP_GLOBAL1:
300 		case OP_GLOBAL2:
301 		case OP_GLOBAL3:
302 		case OP_GLOBAL4:
303 		case OP_GLOBAL5:
304 		case OP_GLOBAL6:
305 		case OP_GLOBAL7:
306 		case OP_FRAGMENTPROGRAMS:
307 		case OP_SOUND:
308 			return qtrue;
309 
310 		default:
311 			return qfalse;
312 	}
313 }
314 
IsOperator(opcode_t oc)315 static qboolean IsOperator(opcode_t oc)
316 {
317 	switch (oc)
318 	{
319 		case OP_LAND:
320 		case OP_LOR:
321 		case OP_GE:
322 		case OP_LE:
323 		case OP_LEQ:
324 		case OP_LNE:
325 		case OP_ADD:
326 		case OP_SUB:
327 		case OP_DIV:
328 		case OP_MOD:
329 		case OP_MUL:
330 		case OP_NEG:
331 		case OP_LT:
332 		case OP_GT:
333 		case OP_TABLE:
334 			return qtrue;
335 
336 		default:
337 			return qfalse;
338 	}
339 }
340 
GetOpPrecedence(opcode_t oc)341 static int GetOpPrecedence(opcode_t oc)
342 {
343 	switch (oc)
344 	{
345 		case OP_LOR:
346 			return 1;
347 
348 		case OP_LAND:
349 			return 2;
350 
351 		case OP_LEQ:
352 		case OP_LNE:
353 			return 3;
354 
355 		case OP_GE:
356 		case OP_LE:
357 		case OP_LT:
358 		case OP_GT:
359 			return 4;
360 
361 		case OP_ADD:
362 		case OP_SUB:
363 			return 5;
364 
365 		case OP_DIV:
366 		case OP_MOD:
367 		case OP_MUL:
368 			return 6;
369 
370 		case OP_NEG:
371 			return 7;
372 
373 		case OP_TABLE:
374 			return 8;
375 
376 		default:
377 			return 0;
378 	}
379 }
380 
ParseExpressionElement(char ** data_p)381 static char *ParseExpressionElement(char **data_p)
382 {
383 	int             c = 0, len;
384 	char           *data;
385 	const char    **punc;
386 	static char     token[MAX_TOKEN_CHARS];
387 
388 	// multiple character punctuation tokens
389 	const char     *punctuation[] = {
390 		"&&", "||", "<=", ">=", "==", "!=", NULL
391 	};
392 
393 	if(!data_p)
394 	{
395 		ri.Error(ERR_FATAL, "ParseExpressionElement: NULL data_p");
396 	}
397 
398 	data = *data_p;
399 	len = 0;
400 	token[0] = 0;
401 
402 	// make sure incoming data is valid
403 	if(!data)
404 	{
405 		*data_p = NULL;
406 		return token;
407 	}
408 
409 	// skip whitespace
410 	while(1)
411 	{
412 		// skip whitespace
413 		while((c = *data) <= ' ')
414 		{
415 			if(!c)
416 			{
417 				*data_p = NULL;
418 				return token;
419 			}
420 			else if(c == '\n')
421 			{
422 				data++;
423 				*data_p = data;
424 				return token;
425 			}
426 			else
427 			{
428 				data++;
429 			}
430 		}
431 
432 		c = *data;
433 
434 		// skip double slash comments
435 		if(c == '/' && data[1] == '/')
436 		{
437 			data += 2;
438 			while(*data && *data != '\n')
439 			{
440 				data++;
441 			}
442 		}
443 		// skip /* */ comments
444 		else if(c == '/' && data[1] == '*')
445 		{
446 			data += 2;
447 			while(*data && (*data != '*' || data[1] != '/'))
448 			{
449 				data++;
450 			}
451 			if(*data)
452 			{
453 				data += 2;
454 			}
455 		}
456 		else
457 		{
458 			// a real token to parse
459 			break;
460 		}
461 	}
462 
463 	// handle quoted strings
464 	if(c == '\"')
465 	{
466 		data++;
467 		while(1)
468 		{
469 			c = *data++;
470 
471 			if((c == '\\') && (*data == '\"'))
472 			{
473 				// allow quoted strings to use \" to indicate the " character
474 				data++;
475 			}
476 			else if(c == '\"' || !c)
477 			{
478 				token[len] = 0;
479 				*data_p = (char *)data;
480 				return token;
481 			}
482 
483 			if(len < MAX_TOKEN_CHARS - 1)
484 			{
485 				token[len] = c;
486 				len++;
487 			}
488 		}
489 	}
490 
491 	// check for a number
492 	if(	(c >= '0' && c <= '9') ||
493 		//(c == '-' && data[1] >= '0' && data[1] <= '9') ||
494 		//(c == '+' && data[1] >= '0' && data[1] <= '9') ||
495 		(c == '.' && data[1] >= '0' && data[1] <= '9'))
496 	{
497 		do
498 		{
499 			if(len < MAX_TOKEN_CHARS - 1)
500 			{
501 				token[len] = c;
502 				len++;
503 			}
504 			data++;
505 
506 			c = *data;
507 		} while((c >= '0' && c <= '9') || c == '.');
508 
509 		// parse the exponent
510 		if(c == 'e' || c == 'E')
511 		{
512 			if(len < MAX_TOKEN_CHARS - 1)
513 			{
514 				token[len] = c;
515 				len++;
516 			}
517 			data++;
518 			c = *data;
519 
520 			if(c == '-' || c == '+')
521 			{
522 				if(len < MAX_TOKEN_CHARS - 1)
523 				{
524 					token[len] = c;
525 					len++;
526 				}
527 				data++;
528 				c = *data;
529 			}
530 
531 			do
532 			{
533 				if(len < MAX_TOKEN_CHARS - 1)
534 				{
535 					token[len] = c;
536 					len++;
537 				}
538 				data++;
539 
540 				c = *data;
541 			} while(c >= '0' && c <= '9');
542 		}
543 
544 		if(len == MAX_TOKEN_CHARS)
545 		{
546 			len = 0;
547 		}
548 		token[len] = 0;
549 
550 		*data_p = (char *)data;
551 		return token;
552 	}
553 
554 	// check for a regular word
555 	if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_'))
556 	{
557 		do
558 		{
559 			if(len < MAX_TOKEN_CHARS - 1)
560 			{
561 				token[len] = c;
562 				len++;
563 			}
564 			data++;
565 
566 			c = *data;
567 		}
568 		while
569 		(
570 			(c >= 'a' && c <= 'z') ||
571 			(c >= 'A' && c <= 'Z') ||
572 			(c == '_') ||
573 			(c >= '0' && c <= '9')
574 		);
575 
576 		if(len == MAX_TOKEN_CHARS)
577 		{
578 			len = 0;
579 		}
580 		token[len] = 0;
581 
582 		*data_p = (char *)data;
583 		return token;
584 	}
585 
586 	// check for multi-character punctuation token
587 	for(punc = punctuation; *punc; punc++)
588 	{
589 		int             l;
590 		int             j;
591 
592 		l = strlen(*punc);
593 		for(j = 0; j < l; j++)
594 		{
595 			if(data[j] != (*punc)[j])
596 			{
597 				break;
598 			}
599 		}
600 		if(j == l)
601 		{
602 			// a valid multi-character punctuation
603 			memcpy(token, *punc, l);
604 			token[l] = 0;
605 			data += l;
606 			*data_p = (char *)data;
607 			return token;
608 		}
609 	}
610 
611 	// single character punctuation
612 	token[0] = *data;
613 	token[1] = 0;
614 	data++;
615 	*data_p = (char *)data;
616 
617 	return token;
618 }
619 
620 /*
621 ===============
622 ParseExpression
623 ===============
624 */
ParseExpression(char ** text,expression_t * exp)625 static void ParseExpression(char **text, expression_t *exp)
626 {
627 	int				i;
628 	char           *token;
629 
630 	expOperation_t  op, op2;
631 
632 	expOperation_t  inFixOps[MAX_EXPRESSION_OPS];
633 	int				numInFixOps;
634 
635 	// convert stack
636 	expOperation_t  tmpOps[MAX_EXPRESSION_OPS];
637 	int				numTmpOps;
638 
639 	numInFixOps = 0;
640 	numTmpOps = 0;
641 
642 	exp->numOps = 0;
643 
644 	// push left parenthesis on the stack
645 	op.type = OP_LPAREN;
646 	op.value = 0;
647 	inFixOps[numInFixOps++] = op;
648 
649 	while(1)
650 	{
651 		token = ParseExpressionElement(text);
652 
653 		if(token[0] == 0 || token[0] == ',')
654 			break;
655 
656 		if(numInFixOps == MAX_EXPRESSION_OPS)
657 		{
658 			ri.Printf(PRINT_ALL, "WARNING: too many arithmetic expression operations in shader '%s'\n", shader.name);
659 			Com_SkipRestOfLine(text);
660 			return;
661 		}
662 
663 		GetOpType(token, &op);
664 
665 		switch(op.type)
666 		{
667 			case OP_BAD:
668 				ri.Printf(PRINT_ALL, "WARNING: unknown token '%s' for arithmetic expression in shader '%s'\n", token, shader.name);
669 				break;
670 
671 			case OP_LBRACKET:
672 				inFixOps[numInFixOps++] = op;
673 
674 				// add extra (
675 				op2.type = OP_LPAREN;
676 				op2.value = 0;
677 				inFixOps[numInFixOps++] = op2;
678 				break;
679 
680 			case OP_RBRACKET:
681 				// add extra )
682 				op2.type = OP_RPAREN;
683 				op2.value = 0;
684 				inFixOps[numInFixOps++] = op2;
685 
686 				inFixOps[numInFixOps++] = op;
687 				break;
688 
689 			case OP_NUM:
690 				op.value = atof(token);
691 				inFixOps[numInFixOps++] = op;
692 				break;
693 
694 			case OP_TABLE:
695 				// value already set by GetOpType
696 				inFixOps[numInFixOps++] = op;
697 				break;
698 
699 			default:
700 				op.value = 0;
701 				inFixOps[numInFixOps++] = op;
702 				break;
703 		}
704 	}
705 
706 	// push right parenthesis on the stack
707 	op.type = OP_RPAREN;
708 	op.value = 0;
709 	inFixOps[numInFixOps++] = op;
710 
711 	for(i = 0; i < (numInFixOps - 1); i++)
712 	{
713 		op = inFixOps[i];
714 		op2 = inFixOps[i + 1];
715 
716 		// convert OP_SUBs that should be unary into OP_NEG
717 		if(op2.type == OP_SUB && op.type != OP_RPAREN && op.type != OP_TABLE && !IsOperand(op.type))
718 			inFixOps[i + 1].type = OP_NEG;
719 	}
720 
721 #if 0
722 	ri.Printf(PRINT_ALL, "infix:\n");
723 	for(i = 0; i < numInFixOps; i++)
724 	{
725 		op = inFixOps[i];
726 
727 		switch (op.type)
728 		{
729 			case OP_NUM:
730 				ri.Printf(PRINT_ALL, "%f ", op.value);
731 				break;
732 
733 			case OP_TABLE:
734 				ri.Printf(PRINT_ALL, "%s ", tr.shaderTables[(int)op.value]->name);
735 				break;
736 
737 			default:
738 				ri.Printf(PRINT_ALL, "%s ", opStrings[op.type].s);
739 				break;
740 		}
741 	}
742 	ri.Printf(PRINT_ALL, "\n");
743 #endif
744 
745 	// http://cis.stvincent.edu/swd/stl/stacks/stacks.html
746 	// http://www.qiksearch.com/articles/cs/infix-postfix/
747 	// http://www.experts-exchange.com/Programming/Programming_Languages/C/Q_20394130.html
748 
749 	//
750 	// convert infix representation to postfix
751 	//
752 
753 	for(i = 0; i < numInFixOps; i++)
754 	{
755 		op = inFixOps[i];
756 
757 		// if current operator in infix is digit
758 		if(IsOperand(op.type))
759 		{
760 			exp->ops[exp->numOps++] = op;
761 		}
762 		// if current operator in infix is left parenthesis
763 		else if(op.type == OP_LPAREN)
764 		{
765 			tmpOps[numTmpOps++] = op;
766 		}
767 		// if current operator in infix is operator
768 		else if(IsOperator(op.type))
769 		{
770 			while(qtrue)
771 			{
772 				if(!numTmpOps)
773 				{
774 					ri.Printf(PRINT_ALL, "WARNING: invalid infix expression in shader '%s'\n", shader.name);
775 					return;
776 				}
777 				else
778 				{
779 					// get top element
780 					op2 = tmpOps[numTmpOps -1];
781 
782 					if(IsOperator(op2.type))
783 					{
784 						if(GetOpPrecedence(op2.type) >= GetOpPrecedence(op.type))
785 						{
786 							exp->ops[exp->numOps++] = op2;
787 							numTmpOps--;
788 						}
789 						else
790 						{
791 							break;
792 						}
793 					}
794 					else
795 					{
796 						break;
797 					}
798 				}
799 			}
800 
801 			tmpOps[numTmpOps++] = op;
802 		}
803 		// if current operator in infix is right parenthesis
804 		else if(op.type == OP_RPAREN)
805 		{
806 			while(qtrue)
807 			{
808 				if(!numTmpOps)
809 				{
810 					ri.Printf(PRINT_ALL, "WARNING: invalid infix expression in shader '%s'\n", shader.name);
811 					return;
812 				}
813 				else
814 				{
815 					// get top element
816 					op2 = tmpOps[numTmpOps -1];
817 
818 					if(op2.type != OP_LPAREN)
819 					{
820 						exp->ops[exp->numOps++] = op2;
821 						numTmpOps--;
822 					}
823 					else
824 					{
825 						numTmpOps--;
826 						break;
827 					}
828 				}
829 			}
830 		}
831 	}
832 
833 	// everything went ok
834 	exp->active = qtrue;
835 
836 #if 0
837 	ri.Printf(PRINT_ALL, "postfix:\n");
838 	for(i = 0; i < exp->numOps; i++)
839 	{
840 		op = exp->ops[i];
841 
842 		switch (op.type)
843 		{
844 			case OP_NUM:
845 				ri.Printf(PRINT_ALL, "%f ", op.value);
846 				break;
847 
848 			case OP_TABLE:
849 				ri.Printf(PRINT_ALL, "%s ", tr.shaderTables[(int)op.value]->name);
850 				break;
851 
852 			default:
853 				ri.Printf(PRINT_ALL, "%s ", opStrings[op.type].s);
854 				break;
855 		}
856 	}
857 	ri.Printf(PRINT_ALL, "\n");
858 #endif
859 }
860 
861 
862 /*
863 ===============
864 NameToAFunc
865 ===============
866 */
NameToAFunc(const char * funcname)867 static unsigned NameToAFunc(const char *funcname)
868 {
869 	if(!Q_stricmp(funcname, "GT0"))
870 	{
871 		return GLS_ATEST_GT_0;
872 	}
873 	else if(!Q_stricmp(funcname, "LT128"))
874 	{
875 		return GLS_ATEST_LT_80;
876 	}
877 	else if(!Q_stricmp(funcname, "GE128"))
878 	{
879 		return GLS_ATEST_GE_80;
880 	}
881 
882 	ri.Printf(PRINT_WARNING, "WARNING: invalid alphaFunc name '%s' in shader '%s'\n", funcname, shader.name);
883 	return 0;
884 }
885 
886 
887 /*
888 ===============
889 NameToSrcBlendMode
890 ===============
891 */
NameToSrcBlendMode(const char * name)892 static int NameToSrcBlendMode(const char *name)
893 {
894 	if(!Q_stricmp(name, "GL_ONE"))
895 	{
896 		return GLS_SRCBLEND_ONE;
897 	}
898 	else if(!Q_stricmp(name, "GL_ZERO"))
899 	{
900 		return GLS_SRCBLEND_ZERO;
901 	}
902 	else if(!Q_stricmp(name, "GL_DST_COLOR"))
903 	{
904 		return GLS_SRCBLEND_DST_COLOR;
905 	}
906 	else if(!Q_stricmp(name, "GL_ONE_MINUS_DST_COLOR"))
907 	{
908 		return GLS_SRCBLEND_ONE_MINUS_DST_COLOR;
909 	}
910 	else if(!Q_stricmp(name, "GL_SRC_ALPHA"))
911 	{
912 		return GLS_SRCBLEND_SRC_ALPHA;
913 	}
914 	else if(!Q_stricmp(name, "GL_ONE_MINUS_SRC_ALPHA"))
915 	{
916 		return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA;
917 	}
918 	else if(!Q_stricmp(name, "GL_DST_ALPHA"))
919 	{
920 		return GLS_SRCBLEND_DST_ALPHA;
921 	}
922 	else if(!Q_stricmp(name, "GL_ONE_MINUS_DST_ALPHA"))
923 	{
924 		return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA;
925 	}
926 	else if(!Q_stricmp(name, "GL_SRC_ALPHA_SATURATE"))
927 	{
928 		return GLS_SRCBLEND_ALPHA_SATURATE;
929 	}
930 
931 	ri.Printf(PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name,
932 			  shader.name);
933 	return GLS_SRCBLEND_ONE;
934 }
935 
936 /*
937 ===============
938 NameToDstBlendMode
939 ===============
940 */
NameToDstBlendMode(const char * name)941 static int NameToDstBlendMode(const char *name)
942 {
943 	if(!Q_stricmp(name, "GL_ONE"))
944 	{
945 		return GLS_DSTBLEND_ONE;
946 	}
947 	else if(!Q_stricmp(name, "GL_ZERO"))
948 	{
949 		return GLS_DSTBLEND_ZERO;
950 	}
951 	else if(!Q_stricmp(name, "GL_SRC_ALPHA"))
952 	{
953 		return GLS_DSTBLEND_SRC_ALPHA;
954 	}
955 	else if(!Q_stricmp(name, "GL_ONE_MINUS_SRC_ALPHA"))
956 	{
957 		return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
958 	}
959 	else if(!Q_stricmp(name, "GL_DST_ALPHA"))
960 	{
961 		return GLS_DSTBLEND_DST_ALPHA;
962 	}
963 	else if(!Q_stricmp(name, "GL_ONE_MINUS_DST_ALPHA"))
964 	{
965 		return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA;
966 	}
967 	else if(!Q_stricmp(name, "GL_SRC_COLOR"))
968 	{
969 		return GLS_DSTBLEND_SRC_COLOR;
970 	}
971 	else if(!Q_stricmp(name, "GL_ONE_MINUS_SRC_COLOR"))
972 	{
973 		return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR;
974 	}
975 
976 	ri.Printf(PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name,
977 			  shader.name);
978 	return GLS_DSTBLEND_ONE;
979 }
980 
981 /*
982 ===============
983 NameToGenFunc
984 ===============
985 */
NameToGenFunc(const char * funcname)986 static genFunc_t NameToGenFunc(const char *funcname)
987 {
988 	if(!Q_stricmp(funcname, "sin"))
989 	{
990 		return GF_SIN;
991 	}
992 	else if(!Q_stricmp(funcname, "square"))
993 	{
994 		return GF_SQUARE;
995 	}
996 	else if(!Q_stricmp(funcname, "triangle"))
997 	{
998 		return GF_TRIANGLE;
999 	}
1000 	else if(!Q_stricmp(funcname, "sawtooth"))
1001 	{
1002 		return GF_SAWTOOTH;
1003 	}
1004 	else if(!Q_stricmp(funcname, "inversesawtooth"))
1005 	{
1006 		return GF_INVERSE_SAWTOOTH;
1007 	}
1008 	else if(!Q_stricmp(funcname, "noise"))
1009 	{
1010 		return GF_NOISE;
1011 	}
1012 
1013 	ri.Printf(PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name);
1014 	return GF_SIN;
1015 }
1016 
1017 
1018 /*
1019 ===================
1020 ParseWaveForm
1021 ===================
1022 */
ParseWaveForm(char ** text,waveForm_t * wave)1023 static void ParseWaveForm(char **text, waveForm_t * wave)
1024 {
1025 	char           *token;
1026 
1027 	token = Com_ParseExt(text, qfalse);
1028 	if(token[0] == 0)
1029 	{
1030 		ri.Printf(PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name);
1031 		return;
1032 	}
1033 	wave->func = NameToGenFunc(token);
1034 
1035 	// BASE, AMP, PHASE, FREQ
1036 	token = Com_ParseExt(text, qfalse);
1037 	if(token[0] == 0)
1038 	{
1039 		ri.Printf(PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name);
1040 		return;
1041 	}
1042 	wave->base = atof(token);
1043 
1044 	token = Com_ParseExt(text, qfalse);
1045 	if(token[0] == 0)
1046 	{
1047 		ri.Printf(PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name);
1048 		return;
1049 	}
1050 	wave->amplitude = atof(token);
1051 
1052 	token = Com_ParseExt(text, qfalse);
1053 	if(token[0] == 0)
1054 	{
1055 		ri.Printf(PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name);
1056 		return;
1057 	}
1058 	wave->phase = atof(token);
1059 
1060 	token = Com_ParseExt(text, qfalse);
1061 	if(token[0] == 0)
1062 	{
1063 		ri.Printf(PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name);
1064 		return;
1065 	}
1066 	wave->frequency = atof(token);
1067 }
1068 
1069 
1070 /*
1071 ===================
1072 ParseTexMod
1073 ===================
1074 */
ParseTexMod(char ** text,shaderStage_t * stage)1075 static qboolean ParseTexMod(char **text, shaderStage_t * stage)
1076 {
1077 	const char     *token;
1078 	texModInfo_t   *tmi;
1079 
1080 	if(stage->bundle[0].numTexMods == TR_MAX_TEXMODS)
1081 	{
1082 		ri.Error(ERR_DROP, "ERROR: too many tcMod stages in shader '%s'\n", shader.name);
1083 		return qfalse;
1084 	}
1085 
1086 	tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
1087 	stage->bundle[0].numTexMods++;
1088 
1089 	token = Com_ParseExt(text, qfalse);
1090 
1091 //	ri.Printf(PRINT_ALL, "using tcMod '%s' in shader '%s'\n", token, shader.name);
1092 
1093 	// turb
1094 	if(!Q_stricmp(token, "turb"))
1095 	{
1096 		token = Com_ParseExt(text, qfalse);
1097 		if(token[0] == 0)
1098 		{
1099 			ri.Printf(PRINT_WARNING, "WARNING: missing tcMod turb parms in shader '%s'\n", shader.name);
1100 			return qfalse;
1101 		}
1102 		tmi->wave.base = atof(token);
1103 		token = Com_ParseExt(text, qfalse);
1104 		if(token[0] == 0)
1105 		{
1106 			ri.Printf(PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name);
1107 			return qfalse;
1108 		}
1109 		tmi->wave.amplitude = atof(token);
1110 		token = Com_ParseExt(text, qfalse);
1111 		if(token[0] == 0)
1112 		{
1113 			ri.Printf(PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name);
1114 			return qfalse;
1115 		}
1116 		tmi->wave.phase = atof(token);
1117 		token = Com_ParseExt(text, qfalse);
1118 		if(token[0] == 0)
1119 		{
1120 			ri.Printf(PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name);
1121 			return qfalse;
1122 		}
1123 		tmi->wave.frequency = atof(token);
1124 
1125 		tmi->type = TMOD_TURBULENT;
1126 	}
1127 	// scale
1128 	else if(!Q_stricmp(token, "scale"))
1129 	{
1130 		token = Com_ParseExt(text, qfalse);
1131 		if(token[0] == 0)
1132 		{
1133 			ri.Printf(PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name);
1134 			return qfalse;
1135 		}
1136 		tmi->scale[0] = atof(token);
1137 
1138 		token = Com_ParseExt(text, qfalse);
1139 		if(token[0] == 0)
1140 		{
1141 			ri.Printf(PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name);
1142 			return qfalse;
1143 		}
1144 		tmi->scale[1] = atof(token);
1145 		tmi->type = TMOD_SCALE;
1146 	}
1147 	// scroll
1148 	else if(!Q_stricmp(token, "scroll"))
1149 	{
1150 		token = Com_ParseExt(text, qfalse);
1151 		if(token[0] == 0)
1152 		{
1153 			ri.Printf(PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name);
1154 			return qfalse;
1155 		}
1156 		tmi->scroll[0] = atof(token);
1157 		token = Com_ParseExt(text, qfalse);
1158 		if(token[0] == 0)
1159 		{
1160 			ri.Printf(PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name);
1161 			return qfalse;
1162 		}
1163 		tmi->scroll[1] = atof(token);
1164 		tmi->type = TMOD_SCROLL;
1165 	}
1166 	// stretch
1167 	else if(!Q_stricmp(token, "stretch"))
1168 	{
1169 		token = Com_ParseExt(text, qfalse);
1170 		if(token[0] == 0)
1171 		{
1172 			ri.Printf(PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name);
1173 			return qfalse;
1174 		}
1175 		tmi->wave.func = NameToGenFunc(token);
1176 
1177 		token = Com_ParseExt(text, qfalse);
1178 		if(token[0] == 0)
1179 		{
1180 			ri.Printf(PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name);
1181 			return qfalse;
1182 		}
1183 		tmi->wave.base = atof(token);
1184 
1185 		token = Com_ParseExt(text, qfalse);
1186 		if(token[0] == 0)
1187 		{
1188 			ri.Printf(PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name);
1189 			return qfalse;
1190 		}
1191 		tmi->wave.amplitude = atof(token);
1192 
1193 		token = Com_ParseExt(text, qfalse);
1194 		if(token[0] == 0)
1195 		{
1196 			ri.Printf(PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name);
1197 			return qfalse;
1198 		}
1199 		tmi->wave.phase = atof(token);
1200 
1201 		token = Com_ParseExt(text, qfalse);
1202 		if(token[0] == 0)
1203 		{
1204 			ri.Printf(PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name);
1205 			return qfalse;
1206 		}
1207 		tmi->wave.frequency = atof(token);
1208 
1209 		tmi->type = TMOD_STRETCH;
1210 	}
1211 	// transform
1212 	else if(!Q_stricmp(token, "transform"))
1213 	{
1214 		token = Com_ParseExt(text, qfalse);
1215 		if(token[0] == 0)
1216 		{
1217 			ri.Printf(PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name);
1218 			return qfalse;
1219 		}
1220 		tmi->matrix[0][0] = atof(token);
1221 
1222 		token = Com_ParseExt(text, qfalse);
1223 		if(token[0] == 0)
1224 		{
1225 			ri.Printf(PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name);
1226 			return qfalse;
1227 		}
1228 		tmi->matrix[0][1] = atof(token);
1229 
1230 		token = Com_ParseExt(text, qfalse);
1231 		if(token[0] == 0)
1232 		{
1233 			ri.Printf(PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name);
1234 			return qfalse;
1235 		}
1236 		tmi->matrix[1][0] = atof(token);
1237 
1238 		token = Com_ParseExt(text, qfalse);
1239 		if(token[0] == 0)
1240 		{
1241 			ri.Printf(PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name);
1242 			return qfalse;
1243 		}
1244 		tmi->matrix[1][1] = atof(token);
1245 
1246 		token = Com_ParseExt(text, qfalse);
1247 		if(token[0] == 0)
1248 		{
1249 			ri.Printf(PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name);
1250 			return qfalse;
1251 		}
1252 		tmi->translate[0] = atof(token);
1253 
1254 		token = Com_ParseExt(text, qfalse);
1255 		if(token[0] == 0)
1256 		{
1257 			ri.Printf(PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name);
1258 			return qfalse;
1259 		}
1260 		tmi->translate[1] = atof(token);
1261 
1262 		tmi->type = TMOD_TRANSFORM;
1263 	}
1264 	// rotate
1265 	else if(!Q_stricmp(token, "rotate"))
1266 	{
1267 		token = Com_ParseExt(text, qfalse);
1268 		if(token[0] == 0)
1269 		{
1270 			ri.Printf(PRINT_WARNING, "WARNING: missing tcMod rotate parms in shader '%s'\n", shader.name);
1271 			return qfalse;
1272 		}
1273 		tmi->rotateSpeed = atof(token);
1274 		tmi->type = TMOD_ROTATE;
1275 	}
1276 	// entityTranslate
1277 	else if(!Q_stricmp(token, "entityTranslate"))
1278 	{
1279 		tmi->type = TMOD_ENTITY_TRANSLATE;
1280 	}
1281 	else
1282 	{
1283 		ri.Printf(PRINT_WARNING, "WARNING: unknown tcMod '%s' in shader '%s'\n", token, shader.name);
1284 		return qfalse;
1285 	}
1286 
1287 	// Tr3B NOTE: some shaders using tcMod are messed up by artists so we need this bugfix
1288 	while(1)
1289 	{
1290 		token = Com_ParseExt(text, qfalse);
1291 
1292 		if(token[0] == 0)
1293 			break;
1294 
1295 		ri.Printf(PRINT_WARNING, "WARNING: obselete tcMod parameter '%s' in shader '%s'\n", token, shader.name);
1296 	}
1297 
1298 	return qtrue;
1299 }
1300 
1301 
1302 
ParseMap(shaderStage_t * stage,char ** text,char * buffer,int bufferSize)1303 static qboolean ParseMap(shaderStage_t * stage, char **text, char *buffer, int bufferSize)
1304 {
1305 	int				len;
1306 	char           *token;
1307 
1308 	// examples
1309 	// map textures/caves/tembrick1crum_local.tga
1310 	// addnormals (textures/caves/tembrick1crum_local.tga, heightmap (textures/caves/tembrick1crum_bmp.tga, 3 ))
1311 	// heightmap( textures/hell/hellbones_d07bbump.tga, 8)
1312 
1313 	while(1)
1314 	{
1315 		token = Com_ParseExt(text, qfalse);
1316 
1317 		if(!token[0])
1318 		{
1319 			// end of line
1320 			break;
1321 		}
1322 
1323 		Q_strcat(buffer, bufferSize, token);
1324 		Q_strcat(buffer, bufferSize, " ");
1325 	}
1326 
1327 	if(!buffer[0])
1328 	{
1329 		ri.Printf(PRINT_WARNING, "WARNING: 'map' missing parameter in shader '%s'\n", shader.name);
1330 		return qfalse;
1331 	}
1332 
1333 	len = strlen(buffer);
1334 	buffer[len -1] = 0;	// replace last ' ' with tailing zero
1335 
1336 	return qtrue;
1337 }
1338 
LoadMap(shaderStage_t * stage,char * buffer)1339 static qboolean LoadMap(shaderStage_t * stage, char *buffer)
1340 {
1341 	char           *token;
1342 	int				imageBits = 0;
1343 	filterType_t	filterType;
1344 	wrapType_t		wrapType;
1345 	char           *buffer_p = &buffer[0];
1346 
1347 	if(!buffer || !buffer[0])
1348 	{
1349 		ri.Printf(PRINT_WARNING, "WARNING: NULL parameter for LoadMap in shader '%s'\n", shader.name);
1350 		return qfalse;
1351 	}
1352 
1353 //	ri.Printf(PRINT_ALL, "LoadMap: buffer '%s'\n", buffer);
1354 
1355 	token = Com_ParseExt(&buffer_p, qfalse);
1356 
1357 	if(!Q_stricmp(token, "$whiteimage") || !Q_stricmp(token, "$white") || !Q_stricmp(token, "_white") || !Q_stricmp(token, "*white"))
1358 	{
1359 		stage->bundle[0].image[0] = tr.whiteImage;
1360 		return qtrue;
1361 	}
1362 	else if(!Q_stricmp(token, "$blackimage") || !Q_stricmp(token, "$black") || !Q_stricmp(token, "_black") || !Q_stricmp(token, "*black"))
1363 	{
1364 		stage->bundle[0].image[0] = tr.blackImage;
1365 		return qtrue;
1366 	}
1367 	else if(!Q_stricmp(token, "$flatimage") || !Q_stricmp(token, "$flat") || !Q_stricmp(token, "_flat") || !Q_stricmp(token, "*flat"))
1368 	{
1369 		stage->bundle[0].image[0] = tr.flatImage;
1370 		return qtrue;
1371 	}
1372 	else if(!Q_stricmp(token, "$lightmap") || !Q_stricmp(token, "_lightmap") || !Q_stricmp(token, "*lightmap"))
1373 	{
1374 		stage->type = ST_LIGHTMAP;
1375 		stage->bundle[0].isLightMap = qtrue;
1376 		return qtrue;
1377 	}
1378 
1379 	// determine image options
1380 	if(stage->overrideNoPicMip || shader.noPicMip)
1381 	{
1382 		imageBits |= IF_NOPICMIP;
1383 	}
1384 
1385 	if(stage->type == ST_NORMALMAP || stage->type == ST_HEATHAZEMAP)
1386 	{
1387 		imageBits |= IF_NORMALMAP;
1388 	}
1389 
1390 	if(stage->overrideFilterType)
1391 	{
1392 		filterType = stage->filterType;
1393 	}
1394 	else
1395 	{
1396 		filterType = shader.filterType;
1397 	}
1398 
1399 	if(stage->overrideWrapType)
1400 	{
1401 		wrapType = stage->wrapType;
1402 	}
1403 	else
1404 	{
1405 		wrapType = shader.wrapType;
1406 	}
1407 
1408 	// try to load the image
1409 	stage->bundle[0].image[0] = R_FindImageFile(buffer, imageBits, filterType, wrapType);
1410 
1411 	if(!stage->bundle[0].image[0])
1412 	{
1413 		ri.Printf(PRINT_WARNING, "WARNING: R_FindImageFile could not find image '%s' in shader '%s'\n", buffer, shader.name);
1414 		return qfalse;
1415 	}
1416 
1417 	return qtrue;
1418 }
1419 
1420 /*
1421 ===================
1422 ParseStage
1423 ===================
1424 */
ParseStage(shaderStage_t * stage,char ** text)1425 static qboolean ParseStage(shaderStage_t * stage, char **text)
1426 {
1427 	char           *token;
1428 	int				colorMaskBits = 0;
1429 	int             depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0;
1430 	qboolean        depthMaskExplicit = qfalse;
1431 	int				imageBits = 0;
1432 	filterType_t	filterType;
1433 	char            buffer[1024] = "";
1434 	qboolean		loadMap = qfalse;
1435 
1436 	while(1)
1437 	{
1438 		token = Com_ParseExt(text, qtrue);
1439 		if(!token[0])
1440 		{
1441 			ri.Printf(PRINT_WARNING, "WARNING: no matching '}' found\n");
1442 			return qfalse;
1443 		}
1444 
1445 		if(token[0] == '}')
1446 		{
1447 			break;
1448 		}
1449 		// if(<condition>)
1450 		else if(!Q_stricmp(token, "if"))
1451 		{
1452 			ParseExpression(text, &stage->ifExp);
1453 		}
1454 		// map <name>
1455 		else if(!Q_stricmp(token, "map"))
1456 		{
1457 			if(!ParseMap(stage, text, buffer, sizeof(buffer)))
1458 			{
1459 				//ri.Printf(PRINT_WARNING, "WARNING: ParseMap could not create '%s' in shader '%s'\n", token, shader.name);
1460 				return qfalse;
1461 			}
1462 			else
1463 			{
1464 				loadMap = qtrue;
1465 			}
1466 		}
1467 		// remoteRenderMap <int> <int>
1468 		else if(!Q_stricmp(token, "remoteRenderMap"))
1469 		{
1470 			ri.Printf(PRINT_WARNING, "WARNING: remoteRenderMap keyword not supported in shader '%s'\n", shader.name);
1471 			Com_SkipRestOfLine(text);
1472 		}
1473 		// mirrorRenderMap <int> <int>
1474 		else if(!Q_stricmp(token, "mirrorRenderMap"))
1475 		{
1476 			ri.Printf(PRINT_WARNING, "WARNING: mirrorRenderMap keyword not supported in shader '%s'\n", shader.name);
1477 			Com_SkipRestOfLine(text);
1478 		}
1479 		// clampmap <name>
1480 		else if(!Q_stricmp(token, "clampmap"))
1481 		{
1482 			token = Com_ParseExt(text, qfalse);
1483 			if(!token[0])
1484 			{
1485 				ri.Printf(PRINT_WARNING, "WARNING: missing parameter for 'clampmap' keyword in shader '%s'\n", shader.name);
1486 				return qfalse;
1487 			}
1488 
1489 			imageBits = 0;
1490 			if(stage->overrideNoPicMip || shader.noPicMip)
1491 			{
1492 				imageBits |= IF_NOPICMIP;
1493 			}
1494 
1495 			if(stage->overrideFilterType)
1496 			{
1497 				filterType = stage->filterType;
1498 			}
1499 			else
1500 			{
1501 				filterType = shader.filterType;
1502 			}
1503 
1504 			stage->bundle[0].image[0] = R_FindImageFile(token, imageBits, filterType, WT_CLAMP);
1505 			if(!stage->bundle[0].image[0])
1506 			{
1507 				ri.Printf(PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name);
1508 				return qfalse;
1509 			}
1510 		}
1511 		// animMap <frequency> <image1> .... <imageN>
1512 		else if(!Q_stricmp(token, "animMap"))
1513 		{
1514 			token = Com_ParseExt(text, qfalse);
1515 			if(!token[0])
1516 			{
1517 				ri.Printf(PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name);
1518 				return qfalse;
1519 			}
1520 			stage->bundle[0].imageAnimationSpeed = atof(token);
1521 
1522 			imageBits = 0;
1523 			if(stage->overrideNoPicMip || shader.noPicMip)
1524 			{
1525 				imageBits |= IF_NOPICMIP;
1526 			}
1527 
1528 			if(stage->overrideFilterType)
1529 			{
1530 				filterType = stage->filterType;
1531 			}
1532 			else
1533 			{
1534 				filterType = shader.filterType;
1535 			}
1536 
1537 			// parse up to MAX_IMAGE_ANIMATIONS animations
1538 			while(1)
1539 			{
1540 				int             num;
1541 
1542 				token = Com_ParseExt(text, qfalse);
1543 				if(!token[0])
1544 				{
1545 					break;
1546 				}
1547 				num = stage->bundle[0].numImageAnimations;
1548 				if(num < MAX_IMAGE_ANIMATIONS)
1549 				{
1550 					stage->bundle[0].image[num] = R_FindImageFile(token, imageBits, filterType, WT_REPEAT);
1551 					if(!stage->bundle[0].image[num])
1552 					{
1553 						ri.Printf(PRINT_WARNING,
1554 								  "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name);
1555 						return qfalse;
1556 					}
1557 					stage->bundle[0].numImageAnimations++;
1558 				}
1559 			}
1560 		}
1561 		else if(!Q_stricmp(token, "videoMap"))
1562 		{
1563 			token = Com_ParseExt(text, qfalse);
1564 			if(!token[0])
1565 			{
1566 				ri.Printf(PRINT_WARNING, "WARNING: missing parameter for 'videoMap' keyword in shader '%s'\n", shader.name);
1567 				return qfalse;
1568 			}
1569 			stage->bundle[0].videoMapHandle = ri.CIN_PlayCinematic(token, 0, 0, 256, 256, (CIN_loop | CIN_silent | CIN_shader));
1570 			if(stage->bundle[0].videoMapHandle != -1)
1571 			{
1572 				stage->bundle[0].isVideoMap = qtrue;
1573 				stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle];
1574 			}
1575 		}
1576 		// soundmap [waveform]
1577 		else if(!Q_stricmp(token, "soundMap"))
1578 		{
1579 			ri.Printf(PRINT_WARNING, "WARNING: soundMap keyword not supported in shader '%s'\n", shader.name);
1580 			Com_SkipRestOfLine(text);
1581 		}
1582 		else if(!Q_stricmp(token, "cubeMap"))
1583 		{
1584 			token = Com_ParseExt(text, qfalse);
1585 			if(!token[0])
1586 			{
1587 				ri.Printf(PRINT_WARNING, "WARNING: missing parameter for 'cubeMap' keyword in shader '%s'\n", shader.name);
1588 				return qfalse;
1589 			}
1590 
1591 			imageBits = 0;
1592 			if(stage->overrideNoPicMip || shader.noPicMip)
1593 			{
1594 				imageBits |= IF_NOPICMIP;
1595 			}
1596 
1597 			if(stage->overrideFilterType)
1598 			{
1599 				filterType = stage->filterType;
1600 			}
1601 			else
1602 			{
1603 				filterType = shader.filterType;
1604 			}
1605 
1606 			stage->bundle[0].image[0] = R_FindCubeImage(token, imageBits, filterType, WT_CLAMP);
1607 			if(!stage->bundle[0].image[0])
1608 			{
1609 				ri.Printf(PRINT_WARNING, "WARNING: R_FindCubeImage could not find '%s' in shader '%s'\n", token, shader.name);
1610 				return qfalse;
1611 			}
1612 		}
1613 		// cameraCubeMap <map>
1614 		else if(!Q_stricmp(token, "cameraCubeMap"))
1615 		{
1616 			ri.Printf(PRINT_WARNING, "WARNING: cameraCubeMap keyword not supported in shader '%s'\n", shader.name);
1617 			Com_SkipRestOfLine(text);
1618 		}
1619 		// alphafunc <func>
1620 		else if(!Q_stricmp(token, "alphaFunc"))
1621 		{
1622 			token = Com_ParseExt(text, qfalse);
1623 			if(!token[0])
1624 			{
1625 				ri.Printf(PRINT_WARNING,
1626 						  "WARNING: missing parameter for 'alphaFunc' keyword in shader '%s'\n", shader.name);
1627 				return qfalse;
1628 			}
1629 
1630 			atestBits = NameToAFunc(token);
1631 		}
1632 		// alphaTest <exp>
1633 		else if(!Q_stricmp(token, "alphaTest"))
1634 		{
1635 			atestBits = GLS_ATEST_GT_CUSTOM;
1636 			ParseExpression(text, &stage->alphaTestExp);
1637 		}
1638 		// depthFunc <func>
1639 		else if(!Q_stricmp(token, "depthfunc"))
1640 		{
1641 			token = Com_ParseExt(text, qfalse);
1642 
1643 			if(!token[0])
1644 			{
1645 				ri.Printf(PRINT_WARNING, "WARNING: missing parameter for 'depthfunc' keyword in shader '%s'\n", shader.name);
1646 				return qfalse;
1647 			}
1648 
1649 			if(!Q_stricmp(token, "lequal"))
1650 			{
1651 				depthFuncBits = 0;
1652 			}
1653 			else if(!Q_stricmp(token, "equal"))
1654 			{
1655 				depthFuncBits = GLS_DEPTHFUNC_EQUAL;
1656 			}
1657 			else
1658 			{
1659 				ri.Printf(PRINT_WARNING, "WARNING: unknown depthfunc '%s' in shader '%s'\n", token, shader.name);
1660 				continue;
1661 			}
1662 		}
1663 		// ignoreAlphaTest
1664 		else if(!Q_stricmp(token, "ignoreAlphaTest"))
1665 		{
1666 			depthFuncBits = 0;
1667 		}
1668 		// nearest
1669 		else if(!Q_stricmp(token, "nearest"))
1670 		{
1671 			stage->overrideFilterType = qtrue;
1672 			stage->filterType = FT_NEAREST;
1673 		}
1674 		// linear
1675 		else if(!Q_stricmp(token, "linear"))
1676 		{
1677 			stage->overrideFilterType = qtrue;
1678 			stage->filterType = FT_LINEAR;
1679 
1680 			stage->overrideNoPicMip = qtrue;
1681 		}
1682 		// noPicMip
1683 		else if(!Q_stricmp(token, "noPicMip"))
1684 		{
1685 			stage->overrideNoPicMip = qtrue;
1686 		}
1687 		// clamp
1688 		else if(!Q_stricmp(token, "clamp"))
1689 		{
1690 			stage->overrideWrapType = qtrue;
1691 			stage->wrapType = WT_CLAMP;
1692 		}
1693 		// edgeClamp
1694 		else if(!Q_stricmp(token, "edgeClamp"))
1695 		{
1696 			stage->overrideWrapType = qtrue;
1697 			stage->wrapType = WT_EDGE_CLAMP;
1698 		}
1699 		// zeroClamp
1700 		else if(!Q_stricmp(token, "zeroClamp"))
1701 		{
1702 			stage->overrideWrapType = qtrue;
1703 			stage->wrapType = WT_ZERO_CLAMP;
1704 		}
1705 		// alphaZeroClamp
1706 		else if(!Q_stricmp(token, "alphaZeroClamp"))
1707 		{
1708 			stage->overrideWrapType = qtrue;
1709 			stage->wrapType = WT_ALPHA_ZERO_CLAMP;
1710 		}
1711 		// noClamp
1712 		else if(!Q_stricmp(token, "noClamp"))
1713 		{
1714 			stage->overrideWrapType = qtrue;
1715 			stage->wrapType = WT_REPEAT;
1716 		}
1717 		// uncompressed
1718 		else if(!Q_stricmp(token, "uncompressed"))
1719 		{
1720 			stage->uncompressed = qtrue;
1721 		}
1722 		// highQuality
1723 		else if(!Q_stricmp(token, "highQuality"))
1724 		{
1725 			stage->highQuality = qtrue;
1726 		}
1727 		// forceHighQuality
1728 		else if(!Q_stricmp(token, "forceHighQuality"))
1729 		{
1730 			stage->forceHighQuality = qtrue;
1731 		}
1732 		// detail
1733 		else if(!Q_stricmp(token, "detail"))
1734 		{
1735 			stage->isDetail = qtrue;
1736 			continue;
1737 		}
1738 		// blendfunc <srcFactor> <dstFactor>
1739 		// or blendfunc <add|filter|blend>
1740 		else if(!Q_stricmp(token, "blendfunc"))
1741 		{
1742 			token = Com_ParseExt(text, qfalse);
1743 			if(token[0] == 0)
1744 			{
1745 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name);
1746 				continue;
1747 			}
1748 			// check for "simple" blends first
1749 			if(!Q_stricmp(token, "add"))
1750 			{
1751 				blendSrcBits = GLS_SRCBLEND_ONE;
1752 				blendDstBits = GLS_DSTBLEND_ONE;
1753 			}
1754 			else if(!Q_stricmp(token, "filter"))
1755 			{
1756 				blendSrcBits = GLS_SRCBLEND_DST_COLOR;
1757 				blendDstBits = GLS_DSTBLEND_ZERO;
1758 			}
1759 			else if(!Q_stricmp(token, "blend"))
1760 			{
1761 				blendSrcBits = GLS_SRCBLEND_SRC_ALPHA;
1762 				blendDstBits = GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
1763 			}
1764 			else
1765 			{
1766 				// complex double blends
1767 				blendSrcBits = NameToSrcBlendMode(token);
1768 
1769 				token = Com_ParseExt(text, qfalse);
1770 				if(token[0] == 0)
1771 				{
1772 					ri.Printf(PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n",
1773 							  shader.name);
1774 					continue;
1775 				}
1776 				blendDstBits = NameToDstBlendMode(token);
1777 			}
1778 
1779 			// clear depth mask for blended surfaces
1780 			if(!depthMaskExplicit)
1781 			{
1782 				depthMaskBits = 0;
1783 			}
1784 		}
1785 		// blend <srcFactor> , <dstFactor>
1786 		// or blend <add | filter | blend>
1787 		// or blend <diffusemap | bumpmap | specularmap | lightmap>
1788 		else if(!Q_stricmp(token, "blend"))
1789 		{
1790 			token = Com_ParseExt(text, qfalse);
1791 			if(token[0] == 0)
1792 			{
1793 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for blend in shader '%s'\n", shader.name);
1794 				continue;
1795 			}
1796 
1797 			// check for "simple" blends first
1798 			if(!Q_stricmp(token, "add"))
1799 			{
1800 				blendSrcBits = GLS_SRCBLEND_ONE;
1801 				blendDstBits = GLS_DSTBLEND_ONE;
1802 			}
1803 			else if(!Q_stricmp(token, "filter"))
1804 			{
1805 				blendSrcBits = GLS_SRCBLEND_DST_COLOR;
1806 				blendDstBits = GLS_DSTBLEND_ZERO;
1807 			}
1808 			else if(!Q_stricmp(token, "modulate"))
1809 			{
1810 				blendSrcBits = GLS_SRCBLEND_DST_COLOR;
1811 				blendDstBits = GLS_DSTBLEND_ZERO;
1812 			}
1813 			else if(!Q_stricmp(token, "blend"))
1814 			{
1815 				blendSrcBits = GLS_SRCBLEND_SRC_ALPHA;
1816 				blendDstBits = GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
1817 			}
1818 			else if(!Q_stricmp(token, "none"))
1819 			{
1820 				blendSrcBits = GLS_SRCBLEND_ZERO;
1821 				blendDstBits = GLS_DSTBLEND_ONE;
1822 			}
1823 			// check for other semantic meanings
1824 			else if(!Q_stricmp(token, "diffuseMap"))
1825 			{
1826 				stage->type = ST_DIFFUSEMAP;
1827 			}
1828 			else if(!Q_stricmp(token, "bumpMap"))
1829 			{
1830 				stage->type = ST_NORMALMAP;
1831 			}
1832 			else if(!Q_stricmp(token, "specularMap"))
1833 			{
1834 				stage->type = ST_SPECULARMAP;
1835 			}
1836 			else
1837 			{
1838 				// complex double blends
1839 				blendSrcBits = NameToSrcBlendMode(token);
1840 
1841 				token = Com_ParseExt(text, qfalse);
1842 				if(token[0] != ',')
1843 				{
1844 					ri.Printf(PRINT_WARNING, "WARNING: expecting ',', found '%s' instead for blend in shader '%s'\n", token, shader.name);
1845 					continue;
1846 				}
1847 
1848 				token = Com_ParseExt(text, qfalse);
1849 				if(token[0] == 0)
1850 				{
1851 					ri.Printf(PRINT_WARNING, "WARNING: missing parm for blend in shader '%s'\n", shader.name);
1852 					continue;
1853 				}
1854 				blendDstBits = NameToDstBlendMode(token);
1855 			}
1856 
1857 			// clear depth mask for blended surfaces
1858 			if(!depthMaskExplicit && stage->type == ST_COLORMAP)
1859 			{
1860 				depthMaskBits = 0;
1861 			}
1862 		}
1863 		// stage <type>
1864 		else if(!Q_stricmp(token, "stage"))
1865 		{
1866 			token = Com_ParseExt(text, qfalse);
1867 			if(token[0] == 0)
1868 			{
1869 				ri.Printf(PRINT_WARNING, "WARNING: missing parameters for stage in shader '%s'\n", shader.name);
1870 				continue;
1871 			}
1872 
1873 			if(!Q_stricmp(token, "colorMap"))
1874 			{
1875 				stage->type = ST_COLORMAP;
1876 			}
1877 			else if(!Q_stricmp(token, "diffuseMap"))
1878 			{
1879 				stage->type = ST_DIFFUSEMAP;
1880 			}
1881 			else if(!Q_stricmp(token, "normalMap") || !Q_stricmp(token, "bumpMap"))
1882 			{
1883 				stage->type = ST_NORMALMAP;
1884 			}
1885 			else if(!Q_stricmp(token, "specularMap"))
1886 			{
1887 				stage->type = ST_SPECULARMAP;
1888 			}
1889 			else if(!Q_stricmp(token, "heathazeMap"))
1890 			{
1891 				stage->type = ST_HEATHAZEMAP;
1892 			}
1893 			else if(!Q_stricmp(token, "glowMap"))
1894 			{
1895 				stage->type = ST_GLOWMAP;
1896 			}
1897 			else if(!Q_stricmp(token, "lightMap"))
1898 			{
1899 				stage->type = ST_LIGHTMAP;
1900 			}
1901 			else if(!Q_stricmp(token, "reflectionMap"))
1902 			{
1903 				stage->type = ST_REFLECTIONMAP;
1904 			}
1905 			else if(!Q_stricmp(token, "refractionMap"))
1906 			{
1907 				stage->type = ST_REFRACTIONMAP;
1908 			}
1909 			else if(!Q_stricmp(token, "dispersionMap"))
1910 			{
1911 				stage->type = ST_DISPERSIONMAP;
1912 			}
1913 			else if(!Q_stricmp(token, "skyboxMap"))
1914 			{
1915 				stage->type = ST_SKYBOXMAP;
1916 			}
1917 			else if(!Q_stricmp(token, "liquidMap"))
1918 			{
1919 				stage->type = ST_LIQUIDMAP;
1920 			}
1921 			else if(!Q_stricmp(token, "attenuationMapXY"))
1922 			{
1923 				stage->type = ST_ATTENUATIONMAP_XY;
1924 			}
1925 			else if(!Q_stricmp(token, "attenuationMapZ"))
1926 			{
1927 				stage->type = ST_ATTENUATIONMAP_Z;
1928 			}
1929 			else
1930 			{
1931 				ri.Printf(PRINT_WARNING, "WARNING: unknown stage parameter '%s' in shader '%s'\n", token, shader.name);
1932 				continue;
1933 			}
1934 		}
1935 		// rgbGen
1936 		else if(!Q_stricmp(token, "rgbGen"))
1937 		{
1938 			token = Com_ParseExt(text, qfalse);
1939 			if(token[0] == 0)
1940 			{
1941 				ri.Printf(PRINT_WARNING, "WARNING: missing parameters for rgbGen in shader '%s'\n",
1942 						  shader.name);
1943 				continue;
1944 			}
1945 
1946 			if(!Q_stricmp(token, "wave"))
1947 			{
1948 				ParseWaveForm(text, &stage->rgbWave);
1949 				stage->rgbGen = CGEN_WAVEFORM;
1950 			}
1951 			else if(!Q_stricmp(token, "const"))
1952 			{
1953 				vec3_t          color;
1954 
1955 				ParseVector(text, 3, color);
1956 				stage->constantColor[0] = 255 * color[0];
1957 				stage->constantColor[1] = 255 * color[1];
1958 				stage->constantColor[2] = 255 * color[2];
1959 
1960 				stage->rgbGen = CGEN_CONST;
1961 			}
1962 			else if(!Q_stricmp(token, "identity"))
1963 			{
1964 				stage->rgbGen = CGEN_IDENTITY;
1965 			}
1966 			else if(!Q_stricmp(token, "identityLighting"))
1967 			{
1968 				stage->rgbGen = CGEN_IDENTITY_LIGHTING;
1969 			}
1970 			else if(!Q_stricmp(token, "entity"))
1971 			{
1972 				stage->rgbGen = CGEN_ENTITY;
1973 			}
1974 			else if(!Q_stricmp(token, "oneMinusEntity"))
1975 			{
1976 				stage->rgbGen = CGEN_ONE_MINUS_ENTITY;
1977 			}
1978 			else if(!Q_stricmp(token, "vertex"))
1979 			{
1980 				stage->rgbGen = CGEN_VERTEX;
1981 				if(stage->alphaGen == 0)
1982 				{
1983 					stage->alphaGen = AGEN_VERTEX;
1984 				}
1985 			}
1986 			else if(!Q_stricmp(token, "exactVertex"))
1987 			{
1988 				stage->rgbGen = CGEN_EXACT_VERTEX;
1989 			}
1990 			else if(!Q_stricmp(token, "lightingDiffuse"))
1991 			{
1992 				stage->type = ST_DIFFUSEMAP;
1993 				stage->rgbGen = CGEN_LIGHTING_DIFFUSE;
1994 			}
1995 			else if(!Q_stricmp(token, "oneMinusVertex"))
1996 			{
1997 				stage->rgbGen = CGEN_ONE_MINUS_VERTEX;
1998 			}
1999 			else
2000 			{
2001 				ri.Printf(PRINT_WARNING, "WARNING: unknown rgbGen parameter '%s' in shader '%s'\n", token, shader.name);
2002 				continue;
2003 			}
2004 		}
2005 		// rgb <arithmetic expression>
2006 		else if(!Q_stricmp(token, "rgb"))
2007 		{
2008 			stage->rgbGen = CGEN_CUSTOM_RGB;
2009 			ParseExpression(text, &stage->rgbExp);
2010 		}
2011 		// red <arithmetic expression>
2012 		else if(!Q_stricmp(token, "red"))
2013 		{
2014 			stage->rgbGen = CGEN_CUSTOM_RGBs;
2015 			ParseExpression(text, &stage->redExp);
2016 		}
2017 		// green <arithmetic expression>
2018 		else if(!Q_stricmp(token, "green"))
2019 		{
2020 			stage->rgbGen = CGEN_CUSTOM_RGBs;
2021 			ParseExpression(text, &stage->greenExp);
2022 		}
2023 		// blue <arithmetic expression>
2024 		else if(!Q_stricmp(token, "blue"))
2025 		{
2026 			stage->rgbGen = CGEN_CUSTOM_RGBs;
2027 			ParseExpression(text, &stage->blueExp);
2028 		}
2029 		// colored
2030 		else if(!Q_stricmp(token, "colored"))
2031 		{
2032 			stage->rgbGen = CGEN_ENTITY;
2033 			stage->alphaGen = AGEN_ENTITY;
2034 		}
2035 		// vertexColor
2036 		else if(!Q_stricmp(token, "vertexColor"))
2037 		{
2038 			ri.Printf(PRINT_WARNING, "WARNING: vertexColor keyword not supported in shader '%s'\n", shader.name);
2039 		}
2040 		// inverseVertexColor
2041 		else if(!Q_stricmp(token, "inverseVertexColor"))
2042 		{
2043 			ri.Printf(PRINT_WARNING, "WARNING: inverseVertexColor keyword not supported in shader '%s'\n", shader.name);
2044 		}
2045 		// alphaGen
2046 		else if(!Q_stricmp(token, "alphaGen"))
2047 		{
2048 			token = Com_ParseExt(text, qfalse);
2049 			if(token[0] == 0)
2050 			{
2051 				ri.Printf(PRINT_WARNING, "WARNING: missing parameters for alphaGen in shader '%s'\n",
2052 						  shader.name);
2053 				continue;
2054 			}
2055 
2056 			if(!Q_stricmp(token, "wave"))
2057 			{
2058 				ParseWaveForm(text, &stage->alphaWave);
2059 				stage->alphaGen = AGEN_WAVEFORM;
2060 			}
2061 			else if(!Q_stricmp(token, "const"))
2062 			{
2063 				token = Com_ParseExt(text, qfalse);
2064 				stage->constantColor[3] = 255 * atof(token);
2065 				stage->alphaGen = AGEN_CONST;
2066 			}
2067 			else if(!Q_stricmp(token, "identity"))
2068 			{
2069 				stage->alphaGen = AGEN_IDENTITY;
2070 			}
2071 			else if(!Q_stricmp(token, "entity"))
2072 			{
2073 				stage->alphaGen = AGEN_ENTITY;
2074 			}
2075 			else if(!Q_stricmp(token, "oneMinusEntity"))
2076 			{
2077 				stage->alphaGen = AGEN_ONE_MINUS_ENTITY;
2078 			}
2079 			else if(!Q_stricmp(token, "vertex"))
2080 			{
2081 				stage->alphaGen = AGEN_VERTEX;
2082 			}
2083 			else if(!Q_stricmp(token, "lightingSpecular"))
2084 			{
2085 				stage->alphaGen = AGEN_LIGHTING_SPECULAR;
2086 			}
2087 			else if(!Q_stricmp(token, "oneMinusVertex"))
2088 			{
2089 				stage->alphaGen = AGEN_ONE_MINUS_VERTEX;
2090 			}
2091 			else if(!Q_stricmp(token, "portal"))
2092 			{
2093 				stage->alphaGen = AGEN_PORTAL;
2094 				token = Com_ParseExt(text, qfalse);
2095 				if(token[0] == 0)
2096 				{
2097 					shader.portalRange = 256;
2098 					ri.Printf(PRINT_WARNING,
2099 							  "WARNING: missing range parameter for alphaGen portal in shader '%s', defaulting to 256\n",
2100 							  shader.name);
2101 				}
2102 				else
2103 				{
2104 					shader.portalRange = atof(token);
2105 				}
2106 			}
2107 			else
2108 			{
2109 				ri.Printf(PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name);
2110 				continue;
2111 			}
2112 		}
2113 		// alpha <arithmetic expression>
2114 		else if(!Q_stricmp(token, "alpha"))
2115 		{
2116 			stage->alphaGen = AGEN_CUSTOM;
2117 			ParseExpression(text, &stage->alphaExp);
2118 		}
2119 		// color <exp>, <exp>, <exp>, <exp>
2120 		else if(!Q_stricmp(token, "color"))
2121 		{
2122 			stage->rgbGen = CGEN_CUSTOM_RGBs;
2123 			stage->alphaGen = AGEN_CUSTOM;
2124 			ParseExpression(text, &stage->redExp);
2125 			ParseExpression(text, &stage->greenExp);
2126 			ParseExpression(text, &stage->blueExp);
2127 			ParseExpression(text, &stage->alphaExp);
2128 		}
2129 		// privatePolygonOffset <float>
2130 		else if(!Q_stricmp(token, "privatePolygonOffset"))
2131 		{
2132 			token = Com_ParseExt(text, qfalse);
2133 			if(!token[0])
2134 			{
2135 				ri.Printf(PRINT_WARNING, "WARNING: missing parameter for 'privatePolygonOffset' keyword in shader '%s'\n", shader.name);
2136 				return qfalse;
2137 			}
2138 
2139 			stage->privatePolygonOffset = qtrue;
2140 			stage->privatePolygonOffsetValue = atof(token);
2141 		}
2142 		// tcGen <function>
2143 		else if(!Q_stricmp(token, "texGen") || !Q_stricmp(token, "tcGen"))
2144 		{
2145 			token = Com_ParseExt(text, qfalse);
2146 			if(token[0] == 0)
2147 			{
2148 				ri.Printf(PRINT_WARNING, "WARNING: missing texgen parm in shader '%s'\n", shader.name);
2149 				continue;
2150 			}
2151 
2152 			if(!Q_stricmp(token, "environment"))
2153 			{
2154 				stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED;
2155 			}
2156 			else if(!Q_stricmp(token, "lightmap"))
2157 			{
2158 				stage->bundle[0].tcGen = TCGEN_LIGHTMAP;
2159 			}
2160 			else if(!Q_stricmp(token, "texture") || !Q_stricmp(token, "base"))
2161 			{
2162 				stage->bundle[0].tcGen = TCGEN_TEXTURE;
2163 			}
2164 			else if(!Q_stricmp(token, "vector"))
2165 			{
2166 				ParseVector(text, 3, stage->bundle[0].tcGenVectors[0]);
2167 				ParseVector(text, 3, stage->bundle[0].tcGenVectors[1]);
2168 
2169 				stage->bundle[0].tcGen = TCGEN_VECTOR;
2170 			}
2171 			else if(!Q_stricmp(token, "reflect"))
2172 			{
2173 				stage->type = ST_REFLECTIONMAP;
2174 			}
2175 			else if(!Q_stricmp(token, "skybox"))
2176 			{
2177 				stage->type = ST_SKYBOXMAP;
2178 			}
2179 			else
2180 			{
2181 				ri.Printf(PRINT_WARNING, "WARNING: unknown texgen parm in shader '%s'\n", shader.name);
2182 			}
2183 		}
2184 		// tcMod <type> <...>
2185 		else if(!Q_stricmp(token, "tcMod"))
2186 		{
2187 			if(!ParseTexMod(text, stage))
2188 			{
2189 				return qfalse;
2190 			}
2191 		}
2192 		// scroll
2193 		else if(!Q_stricmp(token, "scroll") || !Q_stricmp(token, "translate"))
2194 		{
2195 			texModInfo_t   *tmi;
2196 
2197 			if(stage->bundle[0].numTexMods == TR_MAX_TEXMODS)
2198 			{
2199 				ri.Error(ERR_DROP, "ERROR: too many tcMod stages in shader '%s'\n", shader.name);
2200 				return qfalse;
2201 			}
2202 
2203 			tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
2204 			stage->bundle[0].numTexMods++;
2205 
2206 			ParseExpression(text, &tmi->sExp);
2207 			ParseExpression(text, &tmi->tExp);
2208 
2209 			tmi->type = TMOD_SCROLL2;
2210 		}
2211 		// scale
2212 		else if(!Q_stricmp(token, "scale"))
2213 		{
2214 			texModInfo_t   *tmi;
2215 
2216 			if(stage->bundle[0].numTexMods == TR_MAX_TEXMODS)
2217 			{
2218 				ri.Error(ERR_DROP, "ERROR: too many tcMod stages in shader '%s'\n", shader.name);
2219 				return qfalse;
2220 			}
2221 
2222 			tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
2223 			stage->bundle[0].numTexMods++;
2224 
2225 			ParseExpression(text, &tmi->sExp);
2226 			ParseExpression(text, &tmi->tExp);
2227 
2228 			tmi->type = TMOD_SCALE2;
2229 		}
2230 		// centerScale
2231 		else if(!Q_stricmp(token, "centerScale"))
2232 		{
2233 			texModInfo_t   *tmi;
2234 
2235 			if(stage->bundle[0].numTexMods == TR_MAX_TEXMODS)
2236 			{
2237 				ri.Error(ERR_DROP, "ERROR: too many tcMod stages in shader '%s'\n", shader.name);
2238 				return qfalse;
2239 			}
2240 
2241 			tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
2242 			stage->bundle[0].numTexMods++;
2243 
2244 			ParseExpression(text, &tmi->sExp);
2245 			ParseExpression(text, &tmi->tExp);
2246 
2247 			tmi->type = TMOD_CENTERSCALE;
2248 		}
2249 		// shear
2250 		else if(!Q_stricmp(token, "shear"))
2251 		{
2252 			texModInfo_t   *tmi;
2253 
2254 			if(stage->bundle[0].numTexMods == TR_MAX_TEXMODS)
2255 			{
2256 				ri.Error(ERR_DROP, "ERROR: too many tcMod stages in shader '%s'\n", shader.name);
2257 				return qfalse;
2258 			}
2259 
2260 			tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
2261 			stage->bundle[0].numTexMods++;
2262 
2263 			ParseExpression(text, &tmi->sExp);
2264 			ParseExpression(text, &tmi->tExp);
2265 
2266 			tmi->type = TMOD_SHEAR;
2267 		}
2268 		// rotate
2269 		else if(!Q_stricmp(token, "rotate"))
2270 		{
2271 			texModInfo_t   *tmi;
2272 
2273 			if(stage->bundle[0].numTexMods == TR_MAX_TEXMODS)
2274 			{
2275 				ri.Error(ERR_DROP, "ERROR: too many tcMod stages in shader '%s'\n", shader.name);
2276 				return qfalse;
2277 			}
2278 
2279 			tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
2280 			stage->bundle[0].numTexMods++;
2281 
2282 			ParseExpression(text, &tmi->rExp);
2283 
2284 			tmi->type = TMOD_ROTATE2;
2285 		}
2286 		// depthwrite
2287 		else if(!Q_stricmp(token, "depthwrite"))
2288 		{
2289 			depthMaskBits = GLS_DEPTHMASK_TRUE;
2290 			depthMaskExplicit = qtrue;
2291 			continue;
2292 		}
2293 		// maskRed
2294 		else if(!Q_stricmp(token, "maskRed"))
2295 		{
2296 			colorMaskBits |= GLS_REDMASK_FALSE;
2297 		}
2298 		// maskGreen
2299 		else if(!Q_stricmp(token, "maskGreen"))
2300 		{
2301 			colorMaskBits |= GLS_GREENMASK_FALSE;
2302 		}
2303 		// maskBlue
2304 		else if(!Q_stricmp(token, "maskBlue"))
2305 		{
2306 			colorMaskBits |= GLS_BLUEMASK_FALSE;
2307 		}
2308 		// maskAlpha
2309 		else if(!Q_stricmp(token, "maskAlpha"))
2310 		{
2311 			colorMaskBits |= GLS_ALPHAMASK_FALSE;
2312 		}
2313 		// maskColor
2314 		else if(!Q_stricmp(token, "maskColor"))
2315 		{
2316 			colorMaskBits |= GLS_REDMASK_FALSE | GLS_GREENMASK_FALSE | GLS_BLUEMASK_FALSE;
2317 		}
2318 		// maskDepth
2319 		else if(!Q_stricmp(token, "maskDepth"))
2320 		{
2321 			depthMaskBits &= ~GLS_DEPTHMASK_TRUE;
2322 			depthMaskExplicit = qfalse;
2323 		}
2324 		// specularExponent <arithmetic expression>
2325 		else if(!Q_stricmp(token, "specularExponent"))
2326 		{
2327 			ParseExpression(text, &stage->specularExponentExp);
2328 		}
2329 		// refractionIndex <arithmetic expression>
2330 		else if(!Q_stricmp(token, "refractionIndex"))
2331 		{
2332 			ParseExpression(text, &stage->refractionIndexExp);
2333 		}
2334 		// fresnelPower <arithmetic expression>
2335 		else if(!Q_stricmp(token, "fresnelPower"))
2336 		{
2337 			ParseExpression(text, &stage->fresnelPowerExp);
2338 		}
2339 		// fresnelScale <arithmetic expression>
2340 		else if(!Q_stricmp(token, "fresnelScale"))
2341 		{
2342 			ParseExpression(text, &stage->fresnelScaleExp);
2343 		}
2344 		// fresnelBias <arithmetic expression>
2345 		else if(!Q_stricmp(token, "fresnelBias"))
2346 		{
2347 			ParseExpression(text, &stage->fresnelBiasExp);
2348 		}
2349 		// heightScale <arithmetic expression>
2350 		else if(!Q_stricmp(token, "heightScale"))
2351 		{
2352 			ParseExpression(text, &stage->heightScaleExp);
2353 		}
2354 		// heightBias <arithmetic expression>
2355 		else if(!Q_stricmp(token, "heightBias"))
2356 		{
2357 			ParseExpression(text, &stage->heightBiasExp);
2358 		}
2359 		// deformMagnitude <arithmetic expression>
2360 		else if(!Q_stricmp(token, "deformMagnitude"))
2361 		{
2362 			ParseExpression(text, &stage->deformMagnitudeExp);
2363 		}
2364 		// blurMagnitude <arithmetic expression>
2365 		else if(!Q_stricmp(token, "blurMagnitude"))
2366 		{
2367 			ParseExpression(text, &stage->blurMagnitudeExp);
2368 		}
2369 		// fragmentProgram <prog>
2370 		else if(!Q_stricmp(token, "fragmentProgram"))
2371 		{
2372 			ri.Printf(PRINT_WARNING, "WARNING: fragmentProgram keyword not supported in shader '%s'\n", shader.name);
2373 			Com_SkipRestOfLine(text);
2374 		}
2375 		// vertexProgram <prog>
2376 		else if(!Q_stricmp(token, "vertexProgram"))
2377 		{
2378 			ri.Printf(PRINT_WARNING, "WARNING: vertexProgram keyword not supported in shader '%s'\n", shader.name);
2379 			Com_SkipRestOfLine(text);
2380 		}
2381 		// program <prog>
2382 		else if(!Q_stricmp(token, "program"))
2383 		{
2384 			ri.Printf(PRINT_WARNING, "WARNING: program keyword not supported in shader '%s'\n", shader.name);
2385 			Com_SkipRestOfLine(text);
2386 		}
2387 		// vertexParm <index> <exp0> [,exp1] [,exp2] [,exp3]
2388 		else if(!Q_stricmp(token, "vertexParm"))
2389 		{
2390 			ri.Printf(PRINT_WARNING, "WARNING: vertexParm keyword not supported in shader '%s'\n", shader.name);
2391 			Com_SkipRestOfLine(text);
2392 		}
2393 		// fragmentMap <index> [options] <map>
2394 		else if(!Q_stricmp(token, "fragmentMap"))
2395 		{
2396 			ri.Printf(PRINT_WARNING, "WARNING: fragmentMap keyword not supported in shader '%s'\n", shader.name);
2397 			Com_SkipRestOfLine(text);
2398 		}
2399 		// megaTexture <mega>
2400 		else if(!Q_stricmp(token, "megaTexture"))
2401 		{
2402 			ri.Printf(PRINT_WARNING, "WARNING: megaTexture keyword not supported in shader '%s'\n", shader.name);
2403 			Com_SkipRestOfLine(text);
2404 		}
2405 		else
2406 		{
2407 			ri.Printf(PRINT_WARNING, "WARNING: unknown parameter '%s' in shader '%s'\n", token, shader.name);
2408 			Com_SkipRestOfLine(text);
2409 			return qfalse;
2410 		}
2411 	}
2412 
2413 	// parsing succeeded
2414 	stage->active = qtrue;
2415 
2416 	// load image
2417 	if(loadMap && !LoadMap(stage, buffer))
2418 	{
2419 		return qfalse;
2420 	}
2421 
2422 	// if cgen isn't explicitly specified, use either identity or identitylighting
2423 	if(stage->rgbGen == CGEN_BAD)
2424 	{
2425 		if(blendSrcBits == 0 || blendSrcBits == GLS_SRCBLEND_ONE || blendSrcBits == GLS_SRCBLEND_SRC_ALPHA)
2426 		{
2427 			stage->rgbGen = CGEN_IDENTITY_LIGHTING;
2428 		}
2429 		else
2430 		{
2431 			stage->rgbGen = CGEN_IDENTITY;
2432 		}
2433 	}
2434 
2435 	// implicitly assume that a GL_ONE GL_ZERO blend mask disables blending
2436 	if((blendSrcBits == GLS_SRCBLEND_ONE) && (blendDstBits == GLS_DSTBLEND_ZERO))
2437 	{
2438 		blendDstBits = blendSrcBits = 0;
2439 		depthMaskBits = GLS_DEPTHMASK_TRUE;
2440 	}
2441 
2442 	// decide which agens we can skip
2443 	if(stage->alphaGen == CGEN_IDENTITY)
2444 	{
2445 		if(stage->rgbGen == CGEN_IDENTITY || stage->rgbGen == CGEN_LIGHTING_DIFFUSE)
2446 		{
2447 			stage->alphaGen = AGEN_SKIP;
2448 		}
2449 	}
2450 
2451 	// compute state bits
2452 	stage->stateBits = colorMaskBits | depthMaskBits | blendSrcBits | blendDstBits | atestBits | depthFuncBits;
2453 
2454 	return qtrue;
2455 }
2456 
2457 /*
2458 ===============
2459 ParseDeform
2460 
2461 deformVertexes wave <spread> <waveform> <base> <amplitude> <phase> <frequency>
2462 deformVertexes normal <frequency> <amplitude>
2463 deformVertexes move <vector> <waveform> <base> <amplitude> <phase> <frequency>
2464 deformVertexes bulge <bulgeWidth> <bulgeHeight> <bulgeSpeed>
2465 deformVertexes projectionShadow
2466 deformVertexes autoSprite
2467 deformVertexes autoSprite2
2468 deformVertexes text[0-7]
2469 ===============
2470 */
ParseDeform(char ** text)2471 static void ParseDeform(char **text)
2472 {
2473 	char           *token;
2474 	deformStage_t  *ds;
2475 
2476 	token = Com_ParseExt(text, qfalse);
2477 	if(token[0] == 0)
2478 	{
2479 		ri.Printf(PRINT_WARNING, "WARNING: missing deform parm in shader '%s'\n", shader.name);
2480 		return;
2481 	}
2482 
2483 	if(shader.numDeforms == MAX_SHADER_DEFORMS)
2484 	{
2485 		ri.Printf(PRINT_WARNING, "WARNING: MAX_SHADER_DEFORMS in '%s'\n", shader.name);
2486 		return;
2487 	}
2488 
2489 	ds = &shader.deforms[shader.numDeforms];
2490 	shader.numDeforms++;
2491 
2492 	if(!Q_stricmp(token, "projectionShadow"))
2493 	{
2494 		ds->deformation = DEFORM_PROJECTION_SHADOW;
2495 		return;
2496 	}
2497 
2498 	if(!Q_stricmp(token, "autosprite"))
2499 	{
2500 		ds->deformation = DEFORM_AUTOSPRITE;
2501 		return;
2502 	}
2503 
2504 	if(!Q_stricmp(token, "autosprite2"))
2505 	{
2506 		ds->deformation = DEFORM_AUTOSPRITE2;
2507 		return;
2508 	}
2509 
2510 	if(!Q_stricmpn(token, "text", 4))
2511 	{
2512 		int             n;
2513 
2514 		n = token[4] - '0';
2515 		if(n < 0 || n > 7)
2516 		{
2517 			n = 0;
2518 		}
2519 		ds->deformation = DEFORM_TEXT0 + n;
2520 		return;
2521 	}
2522 
2523 	if(!Q_stricmp(token, "bulge"))
2524 	{
2525 		token = Com_ParseExt(text, qfalse);
2526 		if(token[0] == 0)
2527 		{
2528 			ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name);
2529 			return;
2530 		}
2531 		ds->bulgeWidth = atof(token);
2532 
2533 		token = Com_ParseExt(text, qfalse);
2534 		if(token[0] == 0)
2535 		{
2536 			ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name);
2537 			return;
2538 		}
2539 		ds->bulgeHeight = atof(token);
2540 
2541 		token = Com_ParseExt(text, qfalse);
2542 		if(token[0] == 0)
2543 		{
2544 			ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name);
2545 			return;
2546 		}
2547 		ds->bulgeSpeed = atof(token);
2548 
2549 		ds->deformation = DEFORM_BULGE;
2550 		return;
2551 	}
2552 
2553 	if(!Q_stricmp(token, "wave"))
2554 	{
2555 		token = Com_ParseExt(text, qfalse);
2556 		if(token[0] == 0)
2557 		{
2558 			ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name);
2559 			return;
2560 		}
2561 
2562 		if(atof(token) != 0)
2563 		{
2564 			ds->deformationSpread = 1.0f / atof(token);
2565 		}
2566 		else
2567 		{
2568 			ds->deformationSpread = 100.0f;
2569 			ri.Printf(PRINT_WARNING, "WARNING: illegal div value of 0 in deformVertexes command for shader '%s'\n", shader.name);
2570 		}
2571 
2572 		ParseWaveForm(text, &ds->deformationWave);
2573 		ds->deformation = DEFORM_WAVE;
2574 		return;
2575 	}
2576 
2577 	if(!Q_stricmp(token, "normal"))
2578 	{
2579 		token = Com_ParseExt(text, qfalse);
2580 		if(token[0] == 0)
2581 		{
2582 			ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name);
2583 			return;
2584 		}
2585 		ds->deformationWave.amplitude = atof(token);
2586 
2587 		token = Com_ParseExt(text, qfalse);
2588 		if(token[0] == 0)
2589 		{
2590 			ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name);
2591 			return;
2592 		}
2593 		ds->deformationWave.frequency = atof(token);
2594 
2595 		ds->deformation = DEFORM_NORMALS;
2596 		return;
2597 	}
2598 
2599 	if(!Q_stricmp(token, "move"))
2600 	{
2601 		int             i;
2602 
2603 		for(i = 0; i < 3; i++)
2604 		{
2605 			token = Com_ParseExt(text, qfalse);
2606 			if(token[0] == 0)
2607 			{
2608 				ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name);
2609 				return;
2610 			}
2611 			ds->moveVector[i] = atof(token);
2612 		}
2613 
2614 		ParseWaveForm(text, &ds->deformationWave);
2615 		ds->deformation = DEFORM_MOVE;
2616 		return;
2617 	}
2618 
2619 	if(!Q_stricmp(token, "sprite"))
2620 	{
2621 		ds->deformation = DEFORM_SPRITE;
2622 		return;
2623 	}
2624 
2625 	if(!Q_stricmp(token, "flare"))
2626 	{
2627 		token = Com_ParseExt(text, qfalse);
2628 		if(token[0] == 0)
2629 		{
2630 			ri.Printf(PRINT_WARNING, "WARNING: missing deformVertexes flare parm in shader '%s'\n", shader.name);
2631 			return;
2632 		}
2633 		ds->flareSize = atof(token);
2634 		return;
2635 	}
2636 
2637 	ri.Printf(PRINT_WARNING, "WARNING: unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name);
2638 }
2639 
2640 
2641 /*
2642 ===============
2643 ParseSkyParms
2644 
2645 skyParms <outerbox> <cloudheight> <innerbox>
2646 ===============
2647 */
ParseSkyParms(char ** text)2648 static void ParseSkyParms(char **text)
2649 {
2650 	char           *token;
2651 	static char    *suf[6] = { "rt", "bk", "lf", "ft", "up", "dn" };
2652 	char            pathname[MAX_QPATH];
2653 	char            prefix[MAX_QPATH];
2654 	int             i;
2655 
2656 	// outerbox
2657 	token = Com_ParseExt(text, qfalse);
2658 	if(token[0] == 0)
2659 	{
2660 		ri.Printf(PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name);
2661 		return;
2662 	}
2663 	if(strcmp(token, "-"))
2664 	{
2665 		Q_strncpyz(prefix, token, sizeof(prefix));
2666 		for(i = 0; i < 6; i++)
2667 		{
2668 			Com_sprintf(pathname, sizeof(pathname), "%s_%s", prefix, suf[i]);
2669 			shader.sky.outerbox[i] = R_FindImageFile(pathname, IF_NONE, FT_DEFAULT, WT_EDGE_CLAMP);
2670 			if(!shader.sky.outerbox[i])
2671 			{
2672 				shader.sky.outerbox[i] = tr.defaultImage;
2673 			}
2674 		}
2675 	}
2676 
2677 	// cloudheight
2678 	token = Com_ParseExt(text, qfalse);
2679 	if(token[0] == 0)
2680 	{
2681 		ri.Printf(PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name);
2682 		return;
2683 	}
2684 	shader.sky.cloudHeight = atof(token);
2685 	if(!shader.sky.cloudHeight)
2686 	{
2687 		shader.sky.cloudHeight = 512;
2688 	}
2689 	R_InitSkyTexCoords(shader.sky.cloudHeight);
2690 
2691 
2692 	// innerbox
2693 	token = Com_ParseExt(text, qfalse);
2694 	if(token[0] == 0)
2695 	{
2696 		ri.Printf(PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name);
2697 		return;
2698 	}
2699 	if(strcmp(token, "-"))
2700 	{
2701 		Q_strncpyz(prefix, token, sizeof(prefix));
2702 		for(i = 0; i < 6; i++)
2703 		{
2704 			Com_sprintf(pathname, sizeof(pathname), "%s_%s", prefix, suf[i]);
2705 			shader.sky.innerbox[i] = R_FindImageFile(pathname, IF_NONE, FT_DEFAULT, WT_EDGE_CLAMP);
2706 			if(!shader.sky.innerbox[i])
2707 			{
2708 				shader.sky.innerbox[i] = tr.defaultImage;
2709 			}
2710 		}
2711 	}
2712 
2713 	shader.isSky = qtrue;
2714 }
2715 
2716 
2717 /*
2718 =================
2719 ParseSort
2720 =================
2721 */
ParseSort(char ** text)2722 static void ParseSort(char **text)
2723 {
2724 	char           *token;
2725 
2726 	token = Com_ParseExt(text, qfalse);
2727 	if(token[0] == 0)
2728 	{
2729 		ri.Printf(PRINT_WARNING, "WARNING: missing sort parameter in shader '%s'\n", shader.name);
2730 		return;
2731 	}
2732 
2733 	if(!Q_stricmp(token, "portal") || !Q_stricmp(token, "subview"))
2734 	{
2735 		shader.sort = SS_PORTAL;
2736 	}
2737 	else if(!Q_stricmp(token, "sky") || !Q_stricmp(token, "environment"))
2738 	{
2739 		shader.sort = SS_ENVIRONMENT;
2740 	}
2741 	else if(!Q_stricmp(token, "opaque"))
2742 	{
2743 		shader.sort = SS_OPAQUE;
2744 	}
2745 	else if(!Q_stricmp(token, "decal"))
2746 	{
2747 		shader.sort = SS_DECAL;
2748 	}
2749 	else if(!Q_stricmp(token, "seeThrough"))
2750 	{
2751 		shader.sort = SS_SEE_THROUGH;
2752 	}
2753 	else if(!Q_stricmp(token, "banner"))
2754 	{
2755 		shader.sort = SS_BANNER;
2756 	}
2757 	else if(!Q_stricmp(token, "underwater"))
2758 	{
2759 		shader.sort = SS_UNDERWATER;
2760 	}
2761 	else if(!Q_stricmp(token, "far"))
2762 	{
2763 		shader.sort = SS_FAR;
2764 	}
2765 	else if(!Q_stricmp(token, "medium"))
2766 	{
2767 		shader.sort = SS_MEDIUM;
2768 	}
2769 	else if(!Q_stricmp(token, "close"))
2770 	{
2771 		shader.sort = SS_CLOSE;
2772 	}
2773 	else if(!Q_stricmp(token, "additive"))
2774 	{
2775 		shader.sort = SS_BLEND1;
2776 	}
2777 	else if(!Q_stricmp(token, "almostNearest"))
2778 	{
2779 		shader.sort = SS_ALMOST_NEAREST;
2780 	}
2781 	else if(!Q_stricmp(token, "nearest"))
2782 	{
2783 		shader.sort = SS_NEAREST;
2784 	}
2785 	else if(!Q_stricmp(token, "postProcess"))
2786 	{
2787 		shader.sort = SS_POST_PROCESS;
2788 	}
2789 	else
2790 	{
2791 		shader.sort = atof(token);
2792 	}
2793 }
2794 
2795 
2796 
2797 // this table is also present in q3map
2798 
2799 typedef struct
2800 {
2801 	char           *name;
2802 	int             clearSolid, surfaceFlags, contents;
2803 } infoParm_t;
2804 
2805 infoParm_t	infoParms[] = {
2806 	// server relevant contents
2807 	{"water",			1,	0,	CONTENTS_WATER},
2808 	{"slime",			1,	0,	CONTENTS_SLIME},		// mildly damaging
2809 	{"lava",			1,	0,	CONTENTS_LAVA},			// very damaging
2810 	{"playerclip",		1,	0,	CONTENTS_PLAYERCLIP},
2811 	{"monsterclip",		1,	0,	CONTENTS_MONSTERCLIP},
2812 	{"moveableclip",	1,	0,	0},						// FIXME
2813 	{"ikclip",			1,	0,	0},						// FIXME
2814 	{"nodrop",			1,	0,	CONTENTS_NODROP},		// don't drop items or leave bodies (death fog, lava, etc)
2815 	{"nonsolid",		1,	SURF_NONSOLID,	0},			// clears the solid flag
2816 
2817 	{"blood",			1,	0,	CONTENTS_WATER},
2818 
2819 	// utility relevant attributes
2820 	{"origin",			1,	0,	CONTENTS_ORIGIN},		// center of rotating brushes
2821 	{"trans",			0,	0,	CONTENTS_TRANSLUCENT},	// don't eat contained surfaces
2822 	{"translucent",		0,	0,	CONTENTS_TRANSLUCENT},	// don't eat contained surfaces
2823 	{"detail",			0,	0,	CONTENTS_DETAIL},		// don't include in structural bsp
2824 	{"structural",		0,	0,	CONTENTS_STRUCTURAL},	// force into structural bsp even if trnas
2825 	{"areaportal",		1,	0,	CONTENTS_AREAPORTAL},	// divides areas
2826 	{"clusterportal",	1,	0,  CONTENTS_CLUSTERPORTAL},	// for bots
2827 	{"donotenter",  	1,  0,  CONTENTS_DONOTENTER},	// for bots
2828 	{"botclip",			1,  0,  CONTENTS_BOTCLIP},		// for bots
2829 
2830 	{"fog",				1,	0,	CONTENTS_FOG},			// carves surfaces entering
2831 	{"sky",				0,	SURF_SKY,		0},			// emit light from an environment map
2832 	{"lightfilter",		0,	SURF_LIGHTFILTER, 0},		// filter light going through it
2833 	{"alphashadow",		0,	SURF_ALPHASHADOW, 0},		// test light on a per-pixel basis
2834 	{"hint",			0,	SURF_HINT,		0},			// use as a primary splitter
2835 
2836 	// server attributes
2837 	{"slick",			0,	SURF_SLICK,		0},
2838 	{"collision",		0,	SURF_NODRAW,	0},
2839 	{"noimpact",		0,	SURF_NOIMPACT,	0},			// don't make impact explosions or marks
2840 	{"nomarks",			0,	SURF_NOMARKS,	0},			// don't make impact marks, but still explode
2841 	{"nooverlays",		0,	SURF_NOMARKS,	0},			// don't make impact marks, but still explode
2842 	{"ladder",			0,	SURF_LADDER,	0},
2843 	{"nodamage",		0,	SURF_NODAMAGE,	0},
2844 	{"metalsteps",		0,	SURF_METALSTEPS,0},
2845 	{"flesh",			0,	SURF_FLESH,		0},
2846 	{"nosteps",			0,	SURF_NOSTEPS,	0},
2847 
2848 	// drawsurf attributes
2849 	{"nodraw",			0,	SURF_NODRAW,		0},		// don't generate a drawsurface (or a lightmap)
2850 	{"pointlight",		0,	SURF_POINTLIGHT,	0},		// sample lighting at vertexes
2851 	{"nolightmap",		0,	SURF_NOLIGHTMAP,	0},		// don't generate a lightmap
2852 	{"nodlight",		0,	SURF_NODLIGHT,		0},		// don't ever add dynamic lights
2853 	{"dust",			0,	SURF_DUST,			0},		// leave a dust trail when walking on this surface
2854 
2855 	// unsupported Doom3 surface types for sound effects and blood splats
2856 	{"metal",			0,	0,				0},
2857 	{"stone",			0,	0,				0},
2858 	{"wood",			0,	0,				0},
2859 	{"cardboard",		0,	0,				0},
2860 	{"liquid",			0,	0,				0},
2861 	{"glass",			0,	0,				0},
2862 	{"plastic",			0,	0,				0},
2863 	{"ricochet",		0,	0,				0},
2864 	{"surftype10",		0,	0,				0},
2865 	{"surftype11",		0,	0,				0},
2866 	{"surftype12",		0,	0,				0},
2867 	{"surftype13",		0,	0,				0},
2868 	{"surftype14",		0,	0,				0},
2869 	{"surftype15",		0,	0,				0},
2870 
2871 	// other unsupported Doom3 surface types
2872 	{"trigger",			0,	0,				0},
2873 	{"flashlight_trigger",0,0,				0},
2874 	{"aassolid",		0,	0,				0},
2875 	{"aasobstacle",		0,	0,				0},
2876 	{"nullNormal",		0,	0,				0},
2877 	{"discrete",		0,	0,				0},
2878 
2879 };
2880 
2881 /*
2882 ===============
2883 ParseSurfaceParm
2884 
2885 surfaceparm <name>
2886 ===============
2887 */
SurfaceParm(const char * token)2888 static qboolean SurfaceParm(const char *token)
2889 {
2890 	int             numInfoParms = sizeof(infoParms) / sizeof(infoParms[0]);
2891 	int             i;
2892 
2893 	for(i = 0; i < numInfoParms; i++)
2894 	{
2895 		if(!Q_stricmp(token, infoParms[i].name))
2896 		{
2897 			shader.surfaceFlags |= infoParms[i].surfaceFlags;
2898 			shader.contentFlags |= infoParms[i].contents;
2899 #if 0
2900 			if(infoParms[i].clearSolid)
2901 			{
2902 				si->contents &= ~CONTENTS_SOLID;
2903 			}
2904 #endif
2905 			return qtrue;
2906 		}
2907 	}
2908 
2909 	return qfalse;
2910 }
2911 
ParseSurfaceParm(char ** text)2912 static void ParseSurfaceParm(char **text)
2913 {
2914 	char           *token;
2915 
2916 	token = Com_ParseExt(text, qfalse);
2917 	SurfaceParm(token);
2918 }
2919 
ParseDiffuseMap(shaderStage_t * stage,char ** text)2920 static void ParseDiffuseMap(shaderStage_t * stage, char **text)
2921 {
2922 	char			buffer[1024] = "";
2923 
2924 	stage->active = qtrue;
2925 	stage->type = ST_DIFFUSEMAP;
2926 	stage->rgbGen = CGEN_IDENTITY;
2927 	stage->stateBits = GLS_DEFAULT;
2928 
2929 	if(ParseMap(stage, text, buffer, sizeof(buffer)))
2930 	{
2931 		LoadMap(stage, buffer);
2932 	}
2933 }
2934 
ParseNormalMap(shaderStage_t * stage,char ** text)2935 static void ParseNormalMap(shaderStage_t * stage, char **text)
2936 {
2937 	char			buffer[1024] = "";
2938 
2939 	stage->active = qtrue;
2940 	stage->type = ST_NORMALMAP;
2941 	stage->rgbGen = CGEN_IDENTITY;
2942 	stage->stateBits = GLS_DEFAULT;
2943 
2944 	if(ParseMap(stage, text, buffer, sizeof(buffer)))
2945 	{
2946 		LoadMap(stage, buffer);
2947 	}
2948 }
2949 
ParseSpecularMap(shaderStage_t * stage,char ** text)2950 static void ParseSpecularMap(shaderStage_t * stage, char **text)
2951 {
2952 	char			buffer[1024] = "";
2953 
2954 	stage->active = qtrue;
2955 	stage->type = ST_SPECULARMAP;
2956 	stage->rgbGen = CGEN_IDENTITY;
2957 	stage->stateBits = GLS_DEFAULT;
2958 
2959 	if(ParseMap(stage, text, buffer, sizeof(buffer)))
2960 	{
2961 		LoadMap(stage, buffer);
2962 	}
2963 }
2964 
ParseLightMap(shaderStage_t * stage,char ** text)2965 static void ParseLightMap(shaderStage_t * stage, char **text)
2966 {
2967 	char			buffer[1024] = "";
2968 
2969 	stage->active = qtrue;
2970 	stage->type = ST_LIGHTMAP;
2971 	stage->rgbGen = CGEN_IDENTITY;
2972 	stage->stateBits = GLS_DEFAULT;
2973 
2974 	if(ParseMap(stage, text, buffer, sizeof(buffer)))
2975 	{
2976 		LoadMap(stage, buffer);
2977 	}
2978 }
2979 
ParseLightFalloffImage(shaderStage_t * stage,char ** text)2980 static void ParseLightFalloffImage(shaderStage_t * stage, char **text)
2981 {
2982 	char			buffer[1024] = "";
2983 
2984 	stage->active = qtrue;
2985 	stage->type = ST_ATTENUATIONMAP_Z;
2986 	stage->rgbGen = CGEN_IDENTITY;
2987 	stage->stateBits = GLS_DEFAULT;
2988 	stage->overrideWrapType = qtrue;
2989 	stage->wrapType = WT_EDGE_CLAMP;
2990 
2991 	if(ParseMap(stage, text, buffer, sizeof(buffer)))
2992 	{
2993 		LoadMap(stage, buffer);
2994 	}
2995 }
2996 
2997 /*
2998 ====================
2999 FindShaderInShaderText
3000 
3001 Scans the combined text description of all the shader template files for
3002 the given guide name.
3003 
3004 return NULL if not found
3005 
3006 If found, it will return a valid template
3007 =====================
3008 */
FindGuideInGuideText(const char * guideName)3009 static char    *FindGuideInGuideText(const char *guideName)
3010 {
3011 	char           *token, *p;
3012 
3013 	int             i, hash;
3014 
3015 	hash = generateHashValue(guideName, MAX_GUIDETEXT_HASH);
3016 
3017 	for(i = 0; guideTextHashTable[hash][i]; i++)
3018 	{
3019 		p = guideTextHashTable[hash][i];
3020 		token = Com_ParseExt(&p, qtrue);
3021 		if(!Q_stricmp(token, guideName))
3022 		{
3023 			//ri.Printf(PRINT_ALL, "found guide '%s' by hashing\n", guideName);
3024 			return p;
3025 		}
3026 	}
3027 
3028 	p = s_guideText;
3029 
3030 	if(!p)
3031 	{
3032 		return NULL;
3033 	}
3034 
3035 	// look for label
3036 	while(1)
3037 	{
3038 		token = Com_ParseExt(&p, qtrue);
3039 		if(token[0] == 0)
3040 		{
3041 			break;
3042 		}
3043 
3044 		if(Q_stricmp(token, "guide") && Q_stricmp(token, "inlineGuide"))
3045 		{
3046 			ri.Printf(PRINT_WARNING, "WARNING: expected guide or inlineGuide found '%s'\n", token);
3047 			break;
3048 		}
3049 
3050 		// parse guide name
3051 		token = Com_ParseExt(&p, qtrue);
3052 
3053 		if(!Q_stricmp(token, guideName))
3054 		{
3055 			ri.Printf(PRINT_ALL, "found guide '%s' by linear search\n", guideName);
3056 			return p;
3057 		}
3058 
3059 		// skip parameters
3060 		token = Com_ParseExt(&p, qtrue);
3061 		if(Q_stricmp(token, "("))
3062 		{
3063 			ri.Printf(PRINT_WARNING, "WARNING: expected ( found '%s'\n", token);
3064 			break;
3065 		}
3066 
3067 		while(1)
3068 		{
3069 			token = Com_ParseExt(&p, qtrue);
3070 
3071 			if(!token[0])
3072 				break;
3073 
3074 			if(!Q_stricmp(token, ")"))
3075 				break;
3076 		}
3077 
3078 		if(Q_stricmp(token, ")"))
3079 		{
3080 			ri.Printf(PRINT_WARNING, "WARNING: expected ) found '%s'\n", token);
3081 			break;
3082 		}
3083 
3084 		// skip guide body
3085 		Com_SkipBracedSection(&p);
3086 	}
3087 
3088 	return NULL;
3089 }
3090 
3091 
3092 /*
3093 =================
3094 CreateShaderByGuide
3095 =================
3096 */
3097 #define MAX_GUIDE_PARAMETERS 16
CreateShaderByGuide(const char * guideName,char * shaderText)3098 static char    *CreateShaderByGuide(const char *guideName, char *shaderText)
3099 {
3100 	int             i;
3101 	char           *guideText;
3102 	char           *token;
3103 	const char     *p;
3104 	static char     buffer[4096];
3105 	char            name[MAX_QPATH];
3106 	int             numGuideParms;
3107 	char            guideParms[MAX_GUIDE_PARAMETERS][MAX_QPATH];
3108 	int             numShaderParms;
3109 	char            shaderParms[MAX_GUIDE_PARAMETERS][MAX_QPATH];
3110 
3111 	Com_Memset(buffer, 0, sizeof(buffer));
3112 	Com_Memset(guideParms, 0, sizeof(guideParms));
3113 	Com_Memset(shaderParms, 0, sizeof(shaderParms));
3114 
3115 	// attempt to define shader from an explicit parameter file
3116 	guideText = FindGuideInGuideText(guideName);
3117 	if(guideText)
3118 	{
3119 		shader.createdByGuide = qtrue;
3120 
3121 		// parse guide parameters
3122 		numGuideParms = 0;
3123 
3124 		token = Com_ParseExt(&guideText, qtrue);
3125 		if(Q_stricmp(token, "("))
3126 		{
3127 			ri.Printf(PRINT_WARNING, "WARNING: expected ( found '%s'\n", token);
3128 			return NULL;
3129 		}
3130 
3131 		while(1)
3132 		{
3133 			token = Com_ParseExt(&guideText, qtrue);
3134 
3135 			if(!token[0])
3136 				break;
3137 
3138 			if(!Q_stricmp(token, ")"))
3139 				break;
3140 
3141 			if(numGuideParms >= MAX_GUIDE_PARAMETERS -1)
3142 			{
3143 				ri.Printf(PRINT_ALL, "WARNING: more than %i guide parameters are not allowed\n", MAX_GUIDE_PARAMETERS);
3144 				return NULL;
3145 			}
3146 
3147 			//ri.Printf(PRINT_ALL, "guide parameter %i = '%s'\n", numGuideParms, token);
3148 
3149 			Q_strncpyz(guideParms[numGuideParms], token, MAX_QPATH);
3150 			numGuideParms++;
3151 		}
3152 
3153 		if(Q_stricmp(token, ")"))
3154 		{
3155 			ri.Printf(PRINT_ALL, "WARNING: expected ) found '%s'\n", token);
3156 			return NULL;
3157 		}
3158 
3159 		// parse shader parameters
3160 		numShaderParms = 0;
3161 
3162 		token = Com_ParseExt(&shaderText, qtrue);
3163 		if(Q_stricmp(token, "("))
3164 		{
3165 			ri.Printf(PRINT_ALL, "WARNING: expected ( found '%s'\n", token);
3166 			return NULL;
3167 		}
3168 
3169 		while(1)
3170 		{
3171 			token = Com_ParseExt(&shaderText, qtrue);
3172 
3173 			if(!token[0])
3174 				break;
3175 
3176 			if(!Q_stricmp(token, ")"))
3177 				break;
3178 
3179 			if(numShaderParms >= MAX_GUIDE_PARAMETERS -1)
3180 			{
3181 				ri.Printf(PRINT_ALL, "WARNING: more than %i guide parameters are not allowed\n", MAX_GUIDE_PARAMETERS);
3182 				return NULL;
3183 			}
3184 
3185 			//ri.Printf(PRINT_ALL, "shader parameter %i = '%s'\n", numShaderParms, token);
3186 
3187 			Q_strncpyz(shaderParms[numShaderParms], token, MAX_QPATH);
3188 			numShaderParms++;
3189 		}
3190 
3191 		if(Q_stricmp(token, ")"))
3192 		{
3193 			ri.Printf(PRINT_ALL, "WARNING: expected ) found '%s'\n", token);
3194 			return NULL;
3195 		}
3196 
3197 		if(numGuideParms != numShaderParms)
3198 		{
3199 			ri.Printf(PRINT_WARNING, "WARNING: %i numGuideParameters != %i numShaderParameters\n",
3200 										numGuideParms, numShaderParms);
3201 			return NULL;
3202 		}
3203 
3204 		#if 0
3205 		for(i = 0; i < numGuideParms; i++)
3206 		{
3207 			ri.Printf(PRINT_ALL, "guide parameter '%s' = '%s'\n", guideParms[i], shaderParms[i]);
3208 		}
3209 		#endif
3210 
3211 		token = Com_ParseExt(&guideText, qtrue);
3212 		if(Q_stricmp(token, "{"))
3213 		{
3214 			ri.Printf(PRINT_ALL, "WARNING: expected { found '%s'\n", token);
3215 			return NULL;
3216 		}
3217 
3218 		// create buffer
3219 		while(1)
3220 		{
3221 			// begin new line
3222 			token = Com_ParseExt(&guideText, qtrue);
3223 
3224 			if(!token[0])
3225 			{
3226 				ri.Printf(PRINT_WARNING, "WARNING: no concluding '}' in guide %s\n", guideName);
3227 				return NULL;
3228 			}
3229 
3230 			// end of guide definition
3231 			if(token[0] == '}')
3232 			{
3233 				break;
3234 			}
3235 
3236 			Q_strncpyz(name, token, sizeof(name));
3237 
3238 			#if 0
3239 			// adjust name by guide parameters if necessary
3240 			for(i = 0; i < numGuideParms; i++)
3241 			{
3242 				if((p = Q_stristr(name, (const char *)guideParms)))
3243 				{
3244 					//ri.Printf(PRINT_ALL, "guide parameter '%s' = '%s'\n", guideParms[i], shaderParms[i]);
3245 
3246 					Q_strreplace(name, sizeof(name), guideParms[i], shaderParms[i]);
3247 				}
3248 			}
3249 			#endif
3250 
3251 			Q_strcat(buffer, sizeof(buffer), name);
3252 			Q_strcat(buffer, sizeof(buffer), " ");
3253 
3254 			// parse rest of line
3255 			while(1)
3256 			{
3257 				token = Com_ParseExt(&guideText, qfalse);
3258 
3259 				if(!token[0])
3260 				{
3261 					// end of line
3262 					break;
3263 				}
3264 
3265 				Q_strncpyz(name, token, sizeof(name));
3266 
3267 				// adjust name by guide parameters if necessary
3268 				for(i = 0; i < numGuideParms; i++)
3269 				{
3270 					if((p = Q_stristr(name, (const char *)guideParms)))
3271 					{
3272 						//ri.Printf(PRINT_ALL, "guide parameter '%s' = '%s'\n", guideParms[i], shaderParms[i]);
3273 
3274 						Q_strreplace(name, sizeof(name), guideParms[i], shaderParms[i]);
3275 					}
3276 				}
3277 
3278 				Q_strcat(buffer, sizeof(buffer), name);
3279 				Q_strcat(buffer, sizeof(buffer), " ");
3280 			}
3281 
3282 			Q_strcat(buffer, sizeof(buffer), "\n");
3283 		}
3284 
3285 		if(Q_stricmp(token, "}"))
3286 		{
3287 			ri.Printf(PRINT_ALL, "WARNING: expected } found '%s'\n", token);
3288 			return NULL;
3289 		}
3290 
3291 		Q_strcat(buffer, sizeof(buffer), "}");
3292 
3293 		ri.Printf(PRINT_ALL, "----- '%s' -----\n%s\n----------\n", shader.name, buffer);
3294 
3295 		return buffer;
3296 	}
3297 
3298 	return NULL;
3299 }
3300 
3301 /*
3302 =================
3303 ParseShader
3304 
3305 The current text pointer is at the explicit text definition of the
3306 shader.  Parse it into the global shader variable.  Later functions
3307 will optimize it.
3308 =================
3309 */
ParseShader(char * _text)3310 static qboolean ParseShader(char *_text)
3311 {
3312 	char          **text;
3313 	char           *token;
3314 	int             s;
3315 
3316 	s = 0;
3317 	shader.explicitlyDefined = qtrue;
3318 
3319 	text = &_text;
3320 
3321 	token = Com_ParseExt(text, qtrue);
3322 	if(token[0] != '{')
3323 	{
3324 		if(!(_text = CreateShaderByGuide(token, _text)))
3325 		{
3326 			ri.Printf(PRINT_WARNING, "WARNING: couldn't create shader '%s' by template '%s'\n", shader.name, token);
3327 			//ri.Printf(PRINT_WARNING, "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name);
3328 			return qfalse;
3329 		}
3330 		else
3331 		{
3332 			text = &_text;
3333 		}
3334 	}
3335 
3336 	while(1)
3337 	{
3338 		token = Com_ParseExt(text, qtrue);
3339 		if(!token[0])
3340 		{
3341 			ri.Printf(PRINT_WARNING, "WARNING: no concluding '}' in shader %s\n", shader.name);
3342 			return qfalse;
3343 		}
3344 
3345 		// end of shader definition
3346 		if(token[0] == '}')
3347 		{
3348 			break;
3349 		}
3350 		// stage definition
3351 		else if(token[0] == '{')
3352 		{
3353 			if(s >= (MAX_SHADER_STAGES -1))
3354 			{
3355 				ri.Printf(PRINT_WARNING, "WARNING: too many stages in shader %s\n", shader.name);
3356 				return qfalse;
3357 			}
3358 
3359 			if(!ParseStage(&stages[s], text))
3360 			{
3361 				return qfalse;
3362 			}
3363 			stages[s].active = qtrue;
3364 			s++;
3365 			continue;
3366 		}
3367 		// skip stuff that only the QuakeEdRadient needs
3368 		else if(!Q_stricmpn(token, "qer", 3))
3369 		{
3370 			Com_SkipRestOfLine(text);
3371 			continue;
3372 		}
3373 		// skip description
3374 		else if(!Q_stricmp(token, "description"))
3375 		{
3376 			Com_SkipRestOfLine(text);
3377 			continue;
3378 		}
3379 		// skip renderbump
3380 		else if(!Q_stricmp(token, "renderbump"))
3381 		{
3382 			Com_SkipRestOfLine(text);
3383 			continue;
3384 		}
3385 		// skip unsmoothedTangents
3386 		else if(!Q_stricmp(token, "unsmoothedTangents"))
3387 		{
3388 			ri.Printf(PRINT_WARNING, "WARNING: unsmoothedTangents keyword not supported in shader '%s'\n", shader.name);
3389 			continue;
3390 		}
3391 		// skip guiSurf
3392 		else if(!Q_stricmp(token, "guiSurf"))
3393 		{
3394 			Com_SkipRestOfLine(text);
3395 			continue;
3396 		}
3397 		// skip decalInfo
3398 		else if(!Q_stricmp(token, "decalInfo"))
3399 		{
3400 			ri.Printf(PRINT_WARNING, "WARNING: decalInfo keyword not supported in shader '%s'\n", shader.name);
3401 			Com_SkipRestOfLine(text);
3402 			continue;
3403 		}
3404 		// sun parms
3405 		else if(!Q_stricmp(token, "q3map_sun"))
3406 		{
3407 			float           a, b;
3408 
3409 			token = Com_ParseExt(text, qfalse);
3410 			if(!token[0])
3411 			{
3412 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'q3map_sun' keyword in shader '%s'\n", shader.name);
3413 				continue;
3414 			}
3415 			tr.sunLight[0] = atof(token);
3416 
3417 			token = Com_ParseExt(text, qfalse);
3418 			if(!token[0])
3419 			{
3420 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'q3map_sun' keyword in shader '%s'\n", shader.name);
3421 				continue;
3422 			}
3423 			tr.sunLight[1] = atof(token);
3424 
3425 
3426 			token = Com_ParseExt(text, qfalse);
3427 			if(!token[0])
3428 			{
3429 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'q3map_sun' keyword in shader '%s'\n", shader.name);
3430 				continue;
3431 			}
3432 			tr.sunLight[2] = atof(token);
3433 
3434 			VectorNormalize(tr.sunLight);
3435 
3436 			token = Com_ParseExt(text, qfalse);
3437 			if(!token[0])
3438 			{
3439 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'q3map_sun' keyword in shader '%s'\n", shader.name);
3440 				continue;
3441 			}
3442 			a = atof(token);
3443 			VectorScale(tr.sunLight, a, tr.sunLight);
3444 
3445 			token = Com_ParseExt(text, qfalse);
3446 			if(!token[0])
3447 			{
3448 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'q3map_sun' keyword in shader '%s'\n", shader.name);
3449 				continue;
3450 			}
3451 			a = atof(token);
3452 			a = a / 180 * M_PI;
3453 
3454 			token = Com_ParseExt(text, qfalse);
3455 			if(!token[0])
3456 			{
3457 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'q3map_sun' keyword in shader '%s'\n", shader.name);
3458 				continue;
3459 			}
3460 			b = atof(token);
3461 			b = b / 180 * M_PI;
3462 
3463 			tr.sunDirection[0] = cos(a) * cos(b);
3464 			tr.sunDirection[1] = sin(a) * cos(b);
3465 			tr.sunDirection[2] = sin(b);
3466 			continue;
3467 		}
3468 		// noShadows
3469 		else if(!Q_stricmp(token, "noShadows"))
3470 		{
3471 			shader.noShadows = qtrue;
3472 			continue;
3473 		}
3474 		// noSelfShadow
3475 		else if(!Q_stricmp(token, "noSelfShadow"))
3476 		{
3477 			ri.Printf(PRINT_WARNING, "WARNING: noSelfShadow keyword not supported in shader '%s'\n", shader.name);
3478 			continue;
3479 		}
3480 		// forceShadows
3481 		else if(!Q_stricmp(token, "forceShadows"))
3482 		{
3483 			ri.Printf(PRINT_WARNING, "WARNING: forceShadows keyword not supported in shader '%s'\n", shader.name);
3484 			continue;
3485 		}
3486 		// forceOverlays
3487 		else if(!Q_stricmp(token, "forceOverlays"))
3488 		{
3489 			ri.Printf(PRINT_WARNING, "WARNING: forceOverlays keyword not supported in shader '%s'\n", shader.name);
3490 			continue;
3491 		}
3492 		// noPortalFog
3493 		else if(!Q_stricmp(token, "noPortalFog"))
3494 		{
3495 			ri.Printf(PRINT_WARNING, "WARNING: noPortalFog keyword not supported in shader '%s'\n", shader.name);
3496 			continue;
3497 		}
3498 		// fogLight
3499 		else if(!Q_stricmp(token, "fogLight"))
3500 		{
3501 			ri.Printf(PRINT_WARNING, "WARNING: fogLight keyword not supported in shader '%s'\n", shader.name);
3502 			shader.fogLight = qtrue;
3503 			continue;
3504 		}
3505 		// blendLight
3506 		else if(!Q_stricmp(token, "blendLight"))
3507 		{
3508 			ri.Printf(PRINT_WARNING, "WARNING: blendLight keyword not supported in shader '%s'\n", shader.name);
3509 			shader.blendLight = qtrue;
3510 			continue;
3511 		}
3512 		// ambientLight
3513 		else if(!Q_stricmp(token, "ambientLight"))
3514 		{
3515 			ri.Printf(PRINT_WARNING, "WARNING: ambientLight keyword not supported in shader '%s'\n", shader.name);
3516 			shader.ambientLight = qtrue;
3517 			continue;
3518 		}
3519 		// translucent
3520 		else if(!Q_stricmp(token, "translucent"))
3521 		{
3522 			shader.translucent = qtrue;
3523 			continue;
3524 		}
3525 		// forceOpaque
3526 		else if(!Q_stricmp(token, "forceOpaque"))
3527 		{
3528 			shader.forceOpaque = qtrue;
3529 			continue;
3530 		}
3531 		// forceSolid
3532 		else if(!Q_stricmp(token, "forceSolid") || !Q_stricmp(token, "solid"))
3533 		{
3534 			continue;
3535 		}
3536 		else if(!Q_stricmp(token, "deformVertexes") || !Q_stricmp(token, "deform"))
3537 		{
3538 			ParseDeform(text);
3539 			continue;
3540 		}
3541 		else if(!Q_stricmp(token, "tesssize"))
3542 		{
3543 			Com_SkipRestOfLine(text);
3544 			continue;
3545 		}
3546 		// skip noFragment
3547 		if(!Q_stricmp(token, "noFragment"))
3548 		{
3549 			continue;
3550 		}
3551 		else if(!Q_stricmp(token, "clampTime"))
3552 		{
3553 			token = Com_ParseExt(text, qfalse);
3554 			if(token[0])
3555 			{
3556 				shader.clampTime = atof(token);
3557 			}
3558 		}
3559 		// skip stuff that only the q3map needs
3560 		else if(!Q_stricmpn(token, "q3map", 5))
3561 		{
3562 			Com_SkipRestOfLine(text);
3563 			continue;
3564 		}
3565 		// skip stuff that only q3map or the server needs
3566 		else if(!Q_stricmp(token, "surfaceParm"))
3567 		{
3568 			ParseSurfaceParm(text);
3569 			continue;
3570 		}
3571 		// no mip maps
3572 		else if(!Q_stricmp(token, "nomipmaps"))
3573 		{
3574 			shader.filterType = FT_LINEAR;
3575 			shader.noPicMip = qtrue;
3576 			continue;
3577 		}
3578 		// no picmip adjustment
3579 		else if(!Q_stricmp(token, "nopicmip"))
3580 		{
3581 			shader.noPicMip = qtrue;
3582 			continue;
3583 		}
3584 		// polygonOffset
3585 		else if(!Q_stricmp(token, "polygonOffset"))
3586 		{
3587 			shader.polygonOffset = qtrue;
3588 
3589 			token = Com_ParseExt(text, qfalse);
3590 			if(token[0])
3591 			{
3592 				shader.polygonOffsetValue = atof(token);
3593 			}
3594 			continue;
3595 		}
3596 		// entityMergable, allowing sprite surfaces from multiple entities
3597 		// to be merged into one batch.  This is a savings for smoke
3598 		// puffs and blood, but can't be used for anything where the
3599 		// shader calcs (not the surface function) reference the entity color or scroll
3600 		else if(!Q_stricmp(token, "entityMergable"))
3601 		{
3602 			shader.entityMergable = qtrue;
3603 			continue;
3604 		}
3605 		// fogParms
3606 		else if(!Q_stricmp(token, "fogParms"))
3607 		{
3608 			if(!ParseVector(text, 3, shader.fogParms.color))
3609 			{
3610 				return qfalse;
3611 			}
3612 
3613 			token = Com_ParseExt(text, qfalse);
3614 			if(!token[0])
3615 			{
3616 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name);
3617 				continue;
3618 			}
3619 			shader.fogParms.depthForOpaque = atof(token);
3620 
3621 			// skip any old gradient directions
3622 			Com_SkipRestOfLine(text);
3623 			continue;
3624 		}
3625 		// noFog
3626 		else if(!Q_stricmp(token, "noFog"))
3627 		{
3628 			ri.Printf(PRINT_WARNING, "WARNING: noFog keyword not supported in shader '%s'\n", shader.name);
3629 			continue;
3630 		}
3631 		// portal
3632 		else if(!Q_stricmp(token, "portal") || !Q_stricmp(token, "mirror"))
3633 		{
3634 			shader.sort = SS_PORTAL;
3635 			continue;
3636 		}
3637 		// skyparms <cloudheight> <outerbox> <innerbox>
3638 		else if(!Q_stricmp(token, "skyparms"))
3639 		{
3640 			ParseSkyParms(text);
3641 			continue;
3642 		}
3643 		// light <value> determines flaring in q3map, not needed here
3644 		else if(!Q_stricmp(token, "light"))
3645 		{
3646 			token = Com_ParseExt(text, qfalse);
3647 			continue;
3648 		}
3649 		// cull <face>
3650 		else if(!Q_stricmp(token, "cull"))
3651 		{
3652 			token = Com_ParseExt(text, qfalse);
3653 			if(token[0] == 0)
3654 			{
3655 				ri.Printf(PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name);
3656 				continue;
3657 			}
3658 
3659 			if(!Q_stricmp(token, "none") || !Q_stricmp(token, "twoSided") || !Q_stricmp(token, "disable"))
3660 			{
3661 				shader.cullType = CT_TWO_SIDED;
3662 			}
3663 			else if(!Q_stricmp(token, "back") || !Q_stricmp(token, "backside") ||
3664 					!Q_stricmp(token, "backsided"))
3665 			{
3666 				shader.cullType = CT_BACK_SIDED;
3667 			}
3668 			else
3669 			{
3670 				ri.Printf(PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token,
3671 						  shader.name);
3672 			}
3673 			continue;
3674 		}
3675 		// twoSided
3676 		else if(!Q_stricmp(token, "twoSided"))
3677 		{
3678 			shader.cullType = CT_TWO_SIDED;
3679 			continue;
3680 		}
3681 		// backSided
3682 		else if(!Q_stricmp(token, "backSided"))
3683 		{
3684 			shader.cullType = CT_BACK_SIDED;
3685 			continue;
3686 		}
3687 		// clamp
3688 		else if(!Q_stricmp(token, "clamp"))
3689 		{
3690 			shader.wrapType = WT_CLAMP;
3691 			continue;
3692 		}
3693 		// edgeClamp
3694 		else if(!Q_stricmp(token, "edgeClamp"))
3695 		{
3696 			shader.wrapType = WT_EDGE_CLAMP;
3697 			continue;
3698 		}
3699 		// zeroClamp
3700 		else if(!Q_stricmp(token, "zeroclamp"))
3701 		{
3702 			shader.wrapType = WT_ZERO_CLAMP;
3703 			continue;
3704 		}
3705 		// alphaZeroClamp
3706 		else if(!Q_stricmp(token, "alphaZeroClamp"))
3707 		{
3708 			shader.wrapType = WT_ALPHA_ZERO_CLAMP;
3709 			continue;
3710 		}
3711 		// sort
3712 		else if(!Q_stricmp(token, "sort"))
3713 		{
3714 			ParseSort(text);
3715 			continue;
3716 		}
3717 		// spectrum
3718 		else if(!Q_stricmp(token, "spectrum"))
3719 		{
3720 			ri.Printf(PRINT_WARNING, "WARNING: spectrum keyword not supported in shader '%s'\n", shader.name);
3721 
3722 			token = Com_ParseExt(text, qfalse);
3723 			if(!token[0])
3724 			{
3725 				ri.Printf(PRINT_WARNING, "WARNING: missing parm for 'spectrum' keyword in shader '%s'\n", shader.name);
3726 				continue;
3727 			}
3728 			shader.spectrum = qtrue;
3729 			shader.spectrumValue = atoi(token);
3730 			continue;
3731 		}
3732 		// diffuseMap <image>
3733 		else if(!Q_stricmp(token, "diffuseMap"))
3734 		{
3735 			ParseDiffuseMap(&stages[s], text);
3736 			s++;
3737 			continue;
3738 		}
3739 		// normalMap <image>
3740 		else if(!Q_stricmp(token, "normalMap") || !Q_stricmp(token, "bumpMap"))
3741 		{
3742 			ParseNormalMap(&stages[s], text);
3743 			s++;
3744 			continue;
3745 		}
3746 		// specularMap <image>
3747 		else if(!Q_stricmp(token, "specularMap"))
3748 		{
3749 			ParseSpecularMap(&stages[s], text);
3750 			s++;
3751 			continue;
3752 		}
3753 		// lightMap <image>
3754 		else if(!Q_stricmp(token, "lightMap"))
3755 		{
3756 			ParseLightMap(&stages[s], text);
3757 			s++;
3758 			continue;
3759 		}
3760 		// lightFalloffImage <image>
3761 		else if(!Q_stricmp(token, "lightFalloffImage"))
3762 		{
3763 			ParseLightFalloffImage(&stages[s], text);
3764 			s++;
3765 			continue;
3766 		}
3767 		// DECAL_MACRO
3768 		else if(!Q_stricmp(token, "DECAL_MACRO"))
3769 		{
3770 			shader.polygonOffset = qtrue;
3771 			shader.polygonOffsetValue = 1;
3772 			shader.sort = SS_DECAL;
3773 			SurfaceParm("discrete");
3774 			SurfaceParm("noShadows");
3775 			continue;
3776 		}
3777 		else if(SurfaceParm(token))
3778 		{
3779 			continue;
3780 		}
3781 		else
3782 		{
3783 			ri.Printf(PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name);
3784 			return qfalse;
3785 		}
3786 	}
3787 
3788 	// ignore shaders that don't have any stages, unless it is a sky or fog
3789 	if(s == 0 && !shader.forceOpaque && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG))
3790 	{
3791 		return qfalse;
3792 	}
3793 
3794 	return qtrue;
3795 }
3796 
3797 /*
3798 ========================================================================================
3799 
3800 SHADER OPTIMIZATION AND FOGGING
3801 
3802 ========================================================================================
3803 */
3804 
3805 /*
3806 typedef struct
3807 {
3808 	int             blendA;
3809 	int             blendB;
3810 
3811 	int             multitextureEnv;
3812 	int             multitextureBlend;
3813 } collapse_t;
3814 
3815 static collapse_t collapse[] = {
3816 	{0, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO,
3817 	 GL_MODULATE, 0},
3818 
3819 	{0, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR,
3820 	 GL_MODULATE, 0},
3821 
3822 	{GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR,
3823 	 GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR},
3824 
3825 	{GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR,
3826 	 GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR},
3827 
3828 	{GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO,
3829 	 GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR},
3830 
3831 	{GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO,
3832 	 GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR},
3833 
3834 	{0, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE,
3835 	 GL_ADD, 0},
3836 
3837 	{GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE,
3838 	 GL_ADD, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE},
3839 #if 0
3840 	{0, GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_SRCBLEND_SRC_ALPHA,
3841 	 GL_DECAL, 0},
3842 #endif
3843 	{-1}
3844 };
3845 */
3846 
3847 /*
3848 ================
3849 CollapseMultitexture
3850 
3851 Attempt to combine two color stages into a single multitexture stage
3852 FIXME: I think modulated add + modulated add collapses incorrectly
3853 =================
3854 */
CollapseStages(void)3855 static void CollapseStages(void)
3856 {
3857 //	int             abits, bbits;
3858 	int             i, j;
3859 //	textureBundle_t tmpBundle;
3860 
3861 	qboolean		hasDiffuseStage;
3862 	qboolean		hasNormalStage;
3863 	qboolean		hasSpecularStage;
3864 	qboolean		hasLightStage;
3865 	qboolean		hasColorStage;
3866 
3867 	shaderStage_t	tmpDiffuseStage;
3868 	shaderStage_t	tmpNormalStage;
3869 	shaderStage_t	tmpSpecularStage;
3870 	shaderStage_t	tmpLightStage;
3871 
3872 	int				numStages = 0;
3873 	shaderStage_t	tmpStages[MAX_SHADER_STAGES];
3874 	shader_t		tmpShader;
3875 
3876 	if(!qglActiveTextureARB || !r_collapseStages->integer)
3877 	{
3878 		return;
3879 	}
3880 
3881 	//ri.Printf(PRINT_ALL, "...collapsing '%s'\n", shader.name);
3882 
3883 	Com_Memcpy(&tmpStages[0], &stages[0], sizeof(stages));
3884 	Com_Memcpy(&tmpShader, &shader, sizeof(shader));
3885 
3886 	for(j = 0; j < MAX_SHADER_STAGES; j++)
3887 	{
3888 		hasDiffuseStage = qfalse;
3889 		hasNormalStage = qfalse;
3890 		hasSpecularStage = qfalse;
3891 		hasLightStage = qfalse;
3892 		hasColorStage = qfalse;
3893 
3894 		Com_Memset(&tmpDiffuseStage, 0, sizeof(shaderStage_t));
3895 		Com_Memset(&tmpNormalStage, 0, sizeof(shaderStage_t));
3896 		Com_Memset(&tmpSpecularStage, 0, sizeof(shaderStage_t));
3897 		Com_Memset(&tmpLightStage, 0, sizeof(shaderStage_t));
3898 
3899 		if(!stages[j].active)
3900 			continue;
3901 
3902 		if(	stages[j].type == ST_COLORMAP ||
3903 			stages[j].type == ST_HEATHAZEMAP ||
3904 			stages[j].type == ST_GLOWMAP ||
3905 			stages[j].type == ST_REFLECTIONMAP ||
3906 			stages[j].type == ST_REFRACTIONMAP ||
3907 			stages[j].type == ST_DISPERSIONMAP ||
3908 			stages[j].type == ST_SKYBOXMAP ||
3909 			stages[j].type == ST_LIQUIDMAP ||
3910 			stages[j].type == ST_ATTENUATIONMAP_XY ||
3911 			stages[j].type == ST_ATTENUATIONMAP_Z)
3912 		{
3913 			// only merge lighting relevant stages
3914 			tmpStages[numStages] = stages[j];
3915 			numStages++;
3916 			continue;
3917 		}
3918 
3919 		for(i = 0; i < 4; i++)
3920 		{
3921 			if((j+i) >= MAX_SHADER_STAGES)
3922 				continue;
3923 
3924 			if(!stages[j+i].active)
3925 				continue;
3926 
3927 			if(stages[j+i].type == ST_DIFFUSEMAP && !hasDiffuseStage)
3928 			{
3929 				hasDiffuseStage = qtrue;
3930 				tmpDiffuseStage = stages[j+i];
3931 			}
3932 			else if(stages[j+i].type == ST_NORMALMAP && !hasNormalStage)
3933 			{
3934 				hasNormalStage = qtrue;
3935 				tmpNormalStage = stages[j+i];
3936 			}
3937 			else if(stages[j+i].type == ST_SPECULARMAP && !hasSpecularStage)
3938 			{
3939 				hasSpecularStage = qtrue;
3940 				tmpSpecularStage = stages[j+i];
3941 			}
3942 			else if(stages[j+i].type == ST_LIGHTMAP && !hasLightStage)
3943 			{
3944 				hasLightStage = qtrue;
3945 				tmpLightStage = stages[j+i];
3946 			}
3947 			else if(stages[j+i].type == ST_COLORMAP && !hasColorStage)
3948 			{
3949 				hasColorStage = qtrue;
3950 			}
3951 		}
3952 
3953 
3954 		// NOTE: Tr3B - merge as many stages as possible
3955 
3956 		// try to merge diffuse/normal/specular/light
3957 		if(	hasDiffuseStage		&&
3958 			hasNormalStage		&&
3959 			hasSpecularStage	&&
3960 			hasLightStage
3961 		)
3962 		{
3963 			//ri.Printf(PRINT_ALL, "lighting_DBS_radiosity\n");
3964 
3965 			tmpShader.collapseType = COLLAPSE_lighting_DBS_radiosity;
3966 
3967 			tmpStages[numStages] = tmpDiffuseStage;
3968 			tmpStages[numStages].type = ST_COLLAPSE_lighting_DBS_radiosity;
3969 
3970 			tmpStages[numStages].bundle[TB_NORMALMAP] = tmpNormalStage.bundle[0];
3971 			if(!tmpStages[numStages].bundle[TB_NORMALMAP].numTexMods)
3972 			{
3973 				tmpStages[numStages].bundle[TB_NORMALMAP].tcGen = TCGEN_SKIP;
3974 			}
3975 
3976 			tmpStages[numStages].bundle[TB_SPECULARMAP] = tmpSpecularStage.bundle[0];
3977 			if(!tmpStages[numStages].bundle[TB_SPECULARMAP].numTexMods)
3978 			{
3979 				tmpStages[numStages].bundle[TB_SPECULARMAP].tcGen = TCGEN_SKIP;
3980 			}
3981 
3982 			tmpStages[numStages].bundle[TB_LIGHTMAP] = tmpLightStage.bundle[0];
3983 
3984 			tmpStages[numStages].stateBits &= ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS);
3985 
3986 			numStages++;
3987 			j += 3;
3988 			continue;
3989 		}
3990 		// try to merge diffuse/bump/light
3991 		else if(hasDiffuseStage		&&
3992 				hasNormalStage		&&
3993 				hasLightStage
3994 		)
3995 		{
3996 			//ri.Printf(PRINT_ALL, "lighting_DB_radiosity\n");
3997 
3998 			tmpShader.collapseType = COLLAPSE_lighting_DB_radiosity;
3999 
4000 			tmpStages[numStages] = tmpDiffuseStage;
4001 			tmpStages[numStages].type = ST_COLLAPSE_lighting_DB_radiosity;
4002 
4003 			tmpStages[numStages].bundle[TB_NORMALMAP] = tmpNormalStage.bundle[0];
4004 			if(!tmpStages[numStages].bundle[TB_NORMALMAP].numTexMods)
4005 			{
4006 				tmpStages[numStages].bundle[TB_NORMALMAP].tcGen = TCGEN_SKIP;
4007 			}
4008 
4009 			tmpStages[numStages].bundle[TB_LIGHTMAP] = tmpLightStage.bundle[0];
4010 
4011 			tmpStages[numStages].stateBits &= ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS);
4012 
4013 			numStages++;
4014 			j += 2;
4015 			continue;
4016 		}
4017 		else if(hasDiffuseStage		&&
4018 				hasSpecularStage	&&
4019 				hasLightStage
4020 		)
4021 		{
4022 			//ri.Printf(PRINT_ALL, "lighting_D_radiosity\n");
4023 
4024 			tmpShader.collapseType = COLLAPSE_lighting_D_radiosity;
4025 
4026 			tmpStages[numStages] = tmpDiffuseStage;
4027 			tmpStages[numStages].type = ST_COLLAPSE_lighting_D_radiosity;
4028 
4029 			tmpStages[numStages].bundle[TB_LIGHTMAP] = tmpLightStage.bundle[0];
4030 
4031 			tmpStages[numStages].stateBits &= ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS);
4032 
4033 			numStages++;
4034 			j += 2;
4035 			continue;
4036 		}
4037 		// try to merge diffuse/light
4038 		else if(hasDiffuseStage		&&
4039 				hasLightStage
4040 		)
4041 		{
4042 			//ri.Printf(PRINT_ALL, "lighting_D_radiosity\n");
4043 
4044 			tmpShader.collapseType = COLLAPSE_lighting_D_radiosity;
4045 
4046 			tmpStages[numStages] = tmpDiffuseStage;
4047 			tmpStages[numStages].type = ST_COLLAPSE_lighting_D_radiosity;
4048 
4049 			tmpStages[numStages].bundle[TB_LIGHTMAP] = tmpLightStage.bundle[0];
4050 
4051 			tmpStages[numStages].stateBits &= ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS);
4052 
4053 			numStages++;
4054 			j += 1;
4055 			continue;
4056 		}
4057 		// try to merge diffuse/normal/specular
4058 		else if(hasDiffuseStage		&&
4059 				hasNormalStage		&&
4060 				hasSpecularStage	&&
4061 				tmpDiffuseStage.rgbGen == CGEN_LIGHTING_DIFFUSE
4062 		)
4063 		{
4064 			//ri.Printf(PRINT_ALL, "lighting_DBS_direct\n");
4065 
4066 			tmpShader.collapseType = COLLAPSE_lighting_DBS_direct;
4067 
4068 			tmpStages[numStages] = tmpDiffuseStage;
4069 			tmpStages[numStages].type = ST_COLLAPSE_lighting_DBS_direct;
4070 
4071 			tmpStages[numStages].bundle[TB_NORMALMAP] = tmpNormalStage.bundle[0];
4072 			if(!tmpStages[numStages].bundle[TB_NORMALMAP].numTexMods)
4073 			{
4074 				tmpStages[numStages].bundle[TB_NORMALMAP].tcGen = TCGEN_SKIP;
4075 			}
4076 
4077 			tmpStages[numStages].bundle[TB_SPECULARMAP] = tmpSpecularStage.bundle[0];
4078 			if(!tmpStages[numStages].bundle[TB_SPECULARMAP].numTexMods)
4079 			{
4080 				tmpStages[numStages].bundle[TB_SPECULARMAP].tcGen = TCGEN_SKIP;
4081 			}
4082 
4083 			numStages++;
4084 			j += 2;
4085 			continue;
4086 		}
4087 		// try to merge diffuse/normal
4088 		else if(hasDiffuseStage		&&
4089 				hasNormalStage		&&
4090 				tmpDiffuseStage.rgbGen == CGEN_LIGHTING_DIFFUSE
4091 		)
4092 		{
4093 			//ri.Printf(PRINT_ALL, "lighting_DB_direct\n");
4094 
4095 			tmpShader.collapseType = COLLAPSE_lighting_DB_direct;
4096 
4097 			tmpStages[numStages] = tmpDiffuseStage;
4098 			tmpStages[numStages].type = ST_COLLAPSE_lighting_DB_direct;
4099 
4100 			tmpStages[numStages].bundle[TB_NORMALMAP] = tmpNormalStage.bundle[0];
4101 			if(!tmpStages[numStages].bundle[TB_NORMALMAP].numTexMods)
4102 			{
4103 				tmpStages[numStages].bundle[TB_NORMALMAP].tcGen = TCGEN_SKIP;
4104 			}
4105 
4106 			numStages++;
4107 			j += 1;
4108 			continue;
4109 		}
4110 		// try to merge diffuse/normal/specular
4111 		else if(hasDiffuseStage		&&
4112 				hasNormalStage		&&
4113 				hasSpecularStage
4114 		)
4115 		{
4116 			//ri.Printf(PRINT_ALL, "lighting_DBS_generic\n");
4117 
4118 			tmpShader.collapseType = COLLAPSE_lighting_DBS_generic;
4119 
4120 			tmpStages[numStages] = tmpDiffuseStage;
4121 			tmpStages[numStages].type = ST_COLLAPSE_lighting_DBS_generic;
4122 
4123 			tmpStages[numStages].bundle[TB_NORMALMAP] = tmpNormalStage.bundle[0];
4124 			if(!tmpStages[numStages].bundle[TB_NORMALMAP].numTexMods)
4125 			{
4126 				tmpStages[numStages].bundle[TB_NORMALMAP].tcGen = TCGEN_SKIP;
4127 			}
4128 
4129 			tmpStages[numStages].bundle[TB_SPECULARMAP] = tmpSpecularStage.bundle[0];
4130 			if(!tmpStages[numStages].bundle[TB_SPECULARMAP].numTexMods)
4131 			{
4132 				tmpStages[numStages].bundle[TB_SPECULARMAP].tcGen = TCGEN_SKIP;
4133 			}
4134 
4135 			numStages++;
4136 			j += 2;
4137 			continue;
4138 		}
4139 		// try to merge diffuse/normal
4140 		else if(hasDiffuseStage		&&
4141 				hasNormalStage
4142 		)
4143 		{
4144 			//ri.Printf(PRINT_ALL, "lighting_DB_generic\n");
4145 
4146 			tmpShader.collapseType = COLLAPSE_lighting_DB_generic;
4147 
4148 			tmpStages[numStages] = tmpDiffuseStage;
4149 			tmpStages[numStages].type = ST_COLLAPSE_lighting_DB_generic;
4150 
4151 			tmpStages[numStages].bundle[TB_NORMALMAP] = tmpNormalStage.bundle[0];
4152 			if(!tmpStages[numStages].bundle[TB_NORMALMAP].numTexMods)
4153 			{
4154 				tmpStages[numStages].bundle[TB_NORMALMAP].tcGen = TCGEN_SKIP;
4155 			}
4156 
4157 			numStages++;
4158 			j += 1;
4159 			continue;
4160 		}
4161 		// if there was no merge option just copy stage
4162 		else
4163 		{
4164 			tmpStages[numStages] = stages[j];
4165 			numStages++;
4166 		}
4167 		/*
4168 		// try to merge color/color
4169 		else if(stages[0].active					&&
4170 				stages[0].type == ST_COLORMAP		&&
4171 				stages[1].active					&&
4172 				stages[1].type == ST_COLORMAP
4173 		)
4174 		{
4175 			abits = stages[0].stateBits;
4176 			bbits = stages[1].stateBits;
4177 
4178 			// make sure that both stages have identical state other than blend modes
4179 			if((abits & ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE)) !=
4180 			   (bbits & ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE)))
4181 			{
4182 				return;
4183 			}
4184 
4185 			abits &= (GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
4186 			bbits &= (GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
4187 
4188 			// search for a valid multitexture blend function
4189 			for(i = 0; collapse[i].blendA != -1; i++)
4190 			{
4191 				if(abits == collapse[i].blendA && bbits == collapse[i].blendB)
4192 				{
4193 					break;
4194 				}
4195 			}
4196 
4197 			// nothing found
4198 			if(collapse[i].blendA == -1)
4199 			{
4200 				return;
4201 			}
4202 
4203 			// GL_ADD is a separate extension
4204 			if(collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable)
4205 			{
4206 				return;
4207 			}
4208 
4209 			// make sure waveforms have identical parameters
4210 			if((stages[0].rgbGen != stages[1].rgbGen) || (stages[0].alphaGen != stages[1].alphaGen))
4211 			{
4212 				return;
4213 			}
4214 
4215 			// an add collapse can only have identity colors
4216 			if(collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY)
4217 			{
4218 				return;
4219 			}
4220 
4221 			if(stages[0].rgbGen == CGEN_WAVEFORM)
4222 			{
4223 				if(memcmp(&stages[0].rgbWave, &stages[1].rgbWave, sizeof(stages[0].rgbWave)))
4224 				{
4225 					return;
4226 				}
4227 			}
4228 			if(stages[0].alphaGen == CGEN_WAVEFORM)
4229 			{
4230 				if(memcmp(&stages[0].alphaWave, &stages[1].alphaWave, sizeof(stages[0].alphaWave)))
4231 				{
4232 					return;
4233 				}
4234 			}
4235 
4236 			stages[0].bundle[1] = stages[1].bundle[0];
4237 
4238 			// set the new blend state bits
4239 			shader.collapseType = COLLAPSE_Generic_multi;
4240 			shader.collapseTextureEnv = collapse[i].multitextureEnv;
4241 			stages[0].type = ST_COLLAPSE_Generic_multi;
4242 			stages[0].stateBits &= ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
4243 			stages[0].stateBits |= collapse[i].multitextureBlend;
4244 
4245 			// move down subsequent stages
4246 			memmove(&stages[1], &stages[2], sizeof(stages[0]) * (MAX_SHADER_STAGES - 2));
4247 			Com_Memset(&stages[MAX_SHADER_STAGES - 1], 0, sizeof(stages[0]));
4248 
4249 			shader.numStages -= 1;
4250 		}
4251 		*/
4252 	}
4253 
4254 	// move down subsequent stages
4255 	Com_Memset(&tmpStages[numStages], 0, sizeof(stages[0]) * (MAX_SHADER_STAGES - numStages));
4256 	tmpShader.numStages = numStages;
4257 
4258 	// copy result
4259 	Com_Memcpy(&stages[0], &tmpStages[0], sizeof(stages));
4260 	Com_Memcpy(&shader, &tmpShader, sizeof(shader));
4261 }
4262 
4263 /*
4264 ==============
4265 SortNewShader
4266 
4267 Positions the most recently created shader in the tr.sortedShaders[]
4268 array so that the shader->sort key is sorted reletive to the other
4269 shaders.
4270 
4271 Sets shader->sortedIndex
4272 ==============
4273 */
SortNewShader(void)4274 static void SortNewShader(void)
4275 {
4276 	int             i;
4277 	float           sort;
4278 	shader_t       *newShader;
4279 
4280 	newShader = tr.shaders[tr.numShaders - 1];
4281 	sort = newShader->sort;
4282 
4283 	for(i = tr.numShaders - 2; i >= 0; i--)
4284 	{
4285 		if(tr.sortedShaders[i]->sort <= sort)
4286 		{
4287 			break;
4288 		}
4289 		tr.sortedShaders[i + 1] = tr.sortedShaders[i];
4290 		tr.sortedShaders[i + 1]->sortedIndex++;
4291 	}
4292 
4293 	newShader->sortedIndex = i + 1;
4294 	tr.sortedShaders[i + 1] = newShader;
4295 }
4296 
4297 
4298 /*
4299 ====================
4300 GeneratePermanentShader
4301 ====================
4302 */
GeneratePermanentShader(void)4303 static shader_t *GeneratePermanentShader(void)
4304 {
4305 	shader_t       *newShader;
4306 	int             i, b;
4307 	int             size, hash;
4308 
4309 	if(tr.numShaders == MAX_SHADERS)
4310 	{
4311 		ri.Printf(PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n");
4312 		return tr.defaultShader;
4313 	}
4314 
4315 	newShader = ri.Hunk_Alloc(sizeof(shader_t), h_low);
4316 
4317 	*newShader = shader;
4318 
4319 	if(shader.sort <= SS_OPAQUE)
4320 	{
4321 		newShader->fogPass = FP_EQUAL;
4322 	}
4323 	else if(shader.contentFlags & CONTENTS_FOG)
4324 	{
4325 		newShader->fogPass = FP_LE;
4326 	}
4327 
4328 	tr.shaders[tr.numShaders] = newShader;
4329 	newShader->index = tr.numShaders;
4330 
4331 	tr.sortedShaders[tr.numShaders] = newShader;
4332 	newShader->sortedIndex = tr.numShaders;
4333 
4334 	tr.numShaders++;
4335 
4336 	for(i = 0; i < newShader->numStages; i++)
4337 	{
4338 		if(!stages[i].active)
4339 		{
4340 			break;
4341 		}
4342 		newShader->stages[i] = ri.Hunk_Alloc(sizeof(stages[i]), h_low);
4343 		*newShader->stages[i] = stages[i];
4344 
4345 		for(b = 0; b < MAX_TEXTURE_BUNDLES; b++)
4346 		{
4347 			size = newShader->stages[i]->bundle[b].numTexMods * sizeof(texModInfo_t);
4348 			newShader->stages[i]->bundle[b].texMods = ri.Hunk_Alloc(size, h_low);
4349 			Com_Memcpy(newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size);
4350 		}
4351 	}
4352 
4353 	SortNewShader();
4354 
4355 	hash = generateHashValue(newShader->name, FILE_HASH_SIZE);
4356 	newShader->next = shaderHashTable[hash];
4357 	shaderHashTable[hash] = newShader;
4358 
4359 	return newShader;
4360 }
4361 
4362 /*
4363 ====================
4364 GeneratePermanentShaderTable
4365 ====================
4366 */
GeneratePermanentShaderTable(float * values,int numValues)4367 static void GeneratePermanentShaderTable(float *values, int numValues)
4368 {
4369 	shaderTable_t  *newTable;
4370 	int             i;
4371 	int             hash;
4372 
4373 	if(tr.numTables == MAX_SHADER_TABLES)
4374 	{
4375 		ri.Printf(PRINT_WARNING, "WARNING: GeneratePermanentShaderTables - MAX_SHADER_TABLES hit\n");
4376 		return;
4377 	}
4378 
4379 	newTable = ri.Hunk_Alloc(sizeof(shaderTable_t), h_low);
4380 
4381 	*newTable = table;
4382 
4383 	tr.shaderTables[tr.numTables] = newTable;
4384 	newTable->index = tr.numTables;
4385 
4386 	tr.numTables++;
4387 
4388 	newTable->numValues = numValues;
4389 	newTable->values = ri.Hunk_Alloc(sizeof(float) * numValues, h_low);
4390 
4391 //	ri.Printf(PRINT_ALL, "values: \n");
4392 	for(i = 0; i < numValues; i++)
4393 	{
4394 		newTable->values[i] = values[i];
4395 
4396 //		ri.Printf(PRINT_ALL, "%f", newTable->values[i]);
4397 
4398 //		if(i != numValues -1)
4399 //			ri.Printf(PRINT_ALL, ", ");
4400 	}
4401 //	ri.Printf(PRINT_ALL, "\n");
4402 
4403 	hash = generateHashValue(newTable->name, MAX_SHADERTABLE_HASH);
4404 	newTable->next = shaderTableHashTable[hash];
4405 	shaderTableHashTable[hash] = newTable;
4406 }
4407 
4408 /*
4409 =========================
4410 FinishShader
4411 
4412 Returns a freshly allocated shader with all the needed info
4413 from the current global working shader
4414 =========================
4415 */
FinishShader(void)4416 static shader_t *FinishShader(void)
4417 {
4418 	int             stage;
4419 
4420 	// set sky stuff appropriate
4421 	if(shader.isSky)
4422 	{
4423 		shader.sort = SS_ENVIRONMENT;
4424 	}
4425 
4426 	if(shader.forceOpaque)
4427 	{
4428 		shader.sort = SS_OPAQUE;
4429 	}
4430 
4431 	// set polygon offset
4432 	if(shader.polygonOffset && !shader.sort)
4433 	{
4434 		shader.sort = SS_DECAL;
4435 	}
4436 
4437 	// all light materials need at least one z attenuation stage as first stage
4438 	if(shader.type == SHADER_LIGHT)
4439 	{
4440 		if(stages[0].type != ST_ATTENUATIONMAP_Z)
4441 		{
4442 			// move up subsequent stages
4443 			memmove(&stages[1], &stages[0], sizeof(stages[0]) * (MAX_SHADER_STAGES - 1));
4444 
4445 			stages[0].active = qtrue;
4446 			stages[0].type = ST_ATTENUATIONMAP_Z;
4447 			stages[0].rgbGen = CGEN_IDENTITY;
4448 			stages[0].stateBits = GLS_DEFAULT;
4449 			stages[0].overrideWrapType = qtrue;
4450 			stages[0].wrapType = WT_EDGE_CLAMP;
4451 
4452 			LoadMap(&stages[0], "lights/squarelight1a.tga");
4453 		}
4454 
4455 		// force following shader stages to be xy attenuation stages
4456 		for(stage = 1; stage < MAX_SHADER_STAGES; stage++)
4457 		{
4458 			shaderStage_t  *pStage = &stages[stage];
4459 
4460 			if(!pStage->active)
4461 			{
4462 				break;
4463 			}
4464 
4465 			pStage->type = ST_ATTENUATIONMAP_XY;
4466 		}
4467 	}
4468 
4469 	// set appropriate stage information
4470 	for(stage = 0; stage < MAX_SHADER_STAGES; stage++)
4471 	{
4472 		shaderStage_t  *pStage = &stages[stage];
4473 
4474 		if(!pStage->active)
4475 		{
4476 			break;
4477 		}
4478 
4479 		// check for a missing texture
4480 		switch(pStage->type)
4481 		{
4482 			case ST_GLOWMAP:
4483 			case ST_LIQUIDMAP:
4484 				// skip
4485 				break;
4486 
4487 			case ST_COLORMAP:
4488 			default:
4489 			{
4490 				if(!pStage->bundle[0].image[0])
4491 				{
4492 					ri.Printf(PRINT_WARNING, "Shader %s has a colormap stage with no image\n", shader.name);
4493 					pStage->active = qfalse;
4494 					continue;
4495 				}
4496 				break;
4497 			}
4498 
4499 			case ST_DIFFUSEMAP:
4500 			{
4501 				if(!shader.isSky)
4502 				{
4503 					shader.interactLight = qtrue;
4504 				}
4505 
4506 				if(!pStage->bundle[0].image[0])
4507 				{
4508 					ri.Printf(PRINT_WARNING, "Shader %s has a diffusemap stage with no image\n", shader.name);
4509 					pStage->bundle[0].image[0] = tr.defaultImage;
4510 				}
4511 				break;
4512 			}
4513 
4514 			case ST_NORMALMAP:
4515 			{
4516 				if(!pStage->bundle[0].image[0])
4517 				{
4518 					ri.Printf(PRINT_WARNING, "Shader %s has a normalmap stage with no image\n", shader.name);
4519 					pStage->bundle[0].image[0] = tr.flatImage;
4520 				}
4521 				break;
4522 			}
4523 
4524 			case ST_SPECULARMAP:
4525 			{
4526 				if(!pStage->bundle[0].image[0])
4527 				{
4528 					ri.Printf(PRINT_WARNING, "Shader %s has a specularmap stage with no image\n", shader.name);
4529 					pStage->bundle[0].image[0] = tr.blackImage;
4530 				}
4531 				break;
4532 			}
4533 
4534 			case ST_LIGHTMAP:
4535 			{
4536 				if(!pStage->bundle[0].isLightMap)
4537 				{
4538 					ri.Printf(PRINT_WARNING, "Shader %s has a lightmap stage with no image\n", shader.name);
4539 					pStage->active = qfalse;
4540 					continue;
4541 				}
4542 				break;
4543 			}
4544 
4545 			case ST_ATTENUATIONMAP_XY:
4546 			{
4547 				if(!pStage->bundle[0].image[0])
4548 				{
4549 					ri.Printf(PRINT_WARNING, "Shader %s has a xy attenuationmap stage with no image\n", shader.name);
4550 					pStage->active = qfalse;
4551 					continue;
4552 				}
4553 				break;
4554 			}
4555 
4556 			case ST_ATTENUATIONMAP_Z:
4557 			{
4558 				if(!pStage->bundle[0].image[0])
4559 				{
4560 					ri.Printf(PRINT_WARNING, "Shader %s has a z attenuationmap stage with no image\n", shader.name);
4561 					pStage->active = qfalse;
4562 					continue;
4563 				}
4564 				break;
4565 			}
4566 		}
4567 
4568 		// ditch this stage if it's detail and detail textures are disabled
4569 		if(pStage->isDetail && !r_detailTextures->integer)
4570 		{
4571 			if(stage < (MAX_SHADER_STAGES - 1))
4572 			{
4573 				memmove(pStage, pStage + 1, sizeof(*pStage) * (MAX_SHADER_STAGES - stage - 1));
4574 				Com_Memset(pStage + 1, 0, sizeof(*pStage));
4575 			}
4576 			continue;
4577 		}
4578 
4579 		// default texture coordinate generation
4580 		switch(pStage->type)
4581 		{
4582 			case ST_COLORMAP:
4583 			{
4584 				if(pStage->bundle[0].isLightMap)
4585 				{
4586 					if(pStage->bundle[0].tcGen == TCGEN_BAD)
4587 					{
4588 						pStage->bundle[0].tcGen = TCGEN_LIGHTMAP;
4589 					}
4590 				}
4591 				else
4592 				{
4593 					if(pStage->bundle[0].tcGen == TCGEN_BAD)
4594 					{
4595 						pStage->bundle[0].tcGen = TCGEN_TEXTURE;
4596 					}
4597 				}
4598 				break;
4599 			}
4600 
4601 			case ST_DIFFUSEMAP:
4602 			case ST_NORMALMAP:
4603 			case ST_SPECULARMAP:
4604 			case ST_HEATHAZEMAP:
4605 			{
4606 				if(pStage->bundle[0].tcGen == TCGEN_BAD)
4607 				{
4608 					pStage->bundle[0].tcGen = TCGEN_TEXTURE;
4609 				}
4610 				break;
4611 			}
4612 
4613 			case ST_LIGHTMAP:
4614 			{
4615 				if(pStage->bundle[0].tcGen == TCGEN_BAD)
4616 				{
4617 					pStage->bundle[0].tcGen = TCGEN_LIGHTMAP;
4618 				}
4619 				break;
4620 			}
4621 
4622 			case ST_ATTENUATIONMAP_XY:
4623 			case ST_ATTENUATIONMAP_Z:
4624 			{
4625 				if(pStage->bundle[0].tcGen == TCGEN_BAD)
4626 				{
4627 					pStage->bundle[0].tcGen = TCGEN_TEXTURE;
4628 				}
4629 				break;
4630 			}
4631 
4632 			default:
4633 			{
4634 				break;
4635 			}
4636 		}
4637 
4638 		if(shader.forceOpaque)
4639 		{
4640 			pStage->stateBits |= GLS_DEPTHMASK_TRUE;
4641 		}
4642 
4643 		// determine sort order and fog color adjustment
4644 		if((pStage->stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) &&
4645 		   (stages[0].stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)))
4646 		{
4647 			int             blendSrcBits = pStage->stateBits & GLS_SRCBLEND_BITS;
4648 			int             blendDstBits = pStage->stateBits & GLS_DSTBLEND_BITS;
4649 
4650 			// fog color adjustment only works for blend modes that have a contribution
4651 			// that aproaches 0 as the modulate values aproach 0 --
4652 			// GL_ONE, GL_ONE
4653 			// GL_ZERO, GL_ONE_MINUS_SRC_COLOR
4654 			// GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
4655 
4656 			// modulate, additive
4657 			if(((blendSrcBits == GLS_SRCBLEND_ONE) && (blendDstBits == GLS_DSTBLEND_ONE)) ||
4658 			   ((blendSrcBits == GLS_SRCBLEND_ZERO) && (blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR)))
4659 			{
4660 				pStage->adjustColorsForFog = ACFF_MODULATE_RGB;
4661 			}
4662 			// strict blend
4663 			else if((blendSrcBits == GLS_SRCBLEND_SRC_ALPHA) &&
4664 					(blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA))
4665 			{
4666 				pStage->adjustColorsForFog = ACFF_MODULATE_ALPHA;
4667 			}
4668 			// premultiplied alpha
4669 			else if((blendSrcBits == GLS_SRCBLEND_ONE) && (blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA))
4670 			{
4671 				pStage->adjustColorsForFog = ACFF_MODULATE_RGBA;
4672 			}
4673 			else
4674 			{
4675 				// we can't adjust this one correctly, so it won't be exactly correct in fog
4676 			}
4677 
4678 			// don't screw with sort order if this is a portal or environment
4679 			if(!shader.sort)
4680 			{
4681 				// see through item, like a grill or grate
4682 				if(pStage->stateBits & GLS_DEPTHMASK_TRUE)
4683 				{
4684 					shader.sort = SS_SEE_THROUGH;
4685 				}
4686 				else
4687 				{
4688 					shader.sort = SS_BLEND0;
4689 				}
4690 			}
4691 		}
4692 	}
4693 	shader.numStages = stage;
4694 
4695 	// there are times when you will need to manually apply a sort to
4696 	// opaque alpha tested shaders that have later blend passes
4697 	if(!shader.sort)
4698 	{
4699 		if(shader.translucent && !shader.forceOpaque)
4700 			shader.sort = SS_DECAL;
4701 		else
4702 			shader.sort = SS_OPAQUE;
4703 	}
4704 
4705 	// look for multitexture potential
4706 	CollapseStages();
4707 
4708 	// fogonly shaders don't have any normal passes
4709 	if(shader.numStages == 0)
4710 	{
4711 		shader.sort = SS_FOG;
4712 	}
4713 
4714 	return GeneratePermanentShader();
4715 }
4716 
4717 //========================================================================================
4718 
4719 /*
4720 ====================
4721 FindShaderInShaderText
4722 
4723 Scans the combined text description of all the shader files for
4724 the given shader name.
4725 
4726 return NULL if not found
4727 
4728 If found, it will return a valid shader
4729 =====================
4730 */
FindShaderInShaderText(const char * shaderName)4731 static char    *FindShaderInShaderText(const char *shaderName)
4732 {
4733 
4734 	char           *token, *p;
4735 
4736 	int             i, hash;
4737 
4738 	hash = generateHashValue(shaderName, MAX_SHADERTEXT_HASH);
4739 
4740 	for(i = 0; shaderTextHashTable[hash][i]; i++)
4741 	{
4742 		p = shaderTextHashTable[hash][i];
4743 		token = Com_ParseExt(&p, qtrue);
4744 		if(!Q_stricmp(token, shaderName))
4745 		{
4746 			//ri.Printf(PRINT_ALL, "found shader '%s' by hashing\n", shaderName);
4747 			return p;
4748 		}
4749 	}
4750 
4751 	p = s_shaderText;
4752 
4753 	if(!p)
4754 	{
4755 		return NULL;
4756 	}
4757 
4758 	// look for label
4759 	while(1)
4760 	{
4761 		token = Com_ParseExt(&p, qtrue);
4762 		if(token[0] == 0)
4763 		{
4764 			break;
4765 		}
4766 
4767 		if(!Q_stricmp(token, shaderName))
4768 		{
4769 			//ri.Printf(PRINT_ALL, "found shader '%s' by linear search\n", shaderName);
4770 			return p;
4771 		}
4772 		// skip shader tables
4773 		else if(!Q_stricmp(token, "table"))
4774 		{
4775 			// skip table name
4776 			token = Com_ParseExt(&p, qtrue);
4777 
4778 			Com_SkipBracedSection(&p);
4779 		}
4780 		// support shader templates
4781 		else if(!Q_stricmp(token, "guide"))
4782 		{
4783 			// parse shader name
4784 			token = Com_ParseExt(&p, qtrue);
4785 
4786 			if(!Q_stricmp(token, shaderName))
4787 			{
4788 				ri.Printf(PRINT_ALL, "found shader '%s' by linear search\n", shaderName);
4789 				return p;
4790 			}
4791 
4792 			// skip guide name
4793 			token = Com_ParseExt(&p, qtrue);
4794 
4795 			// skip parameters
4796 			token = Com_ParseExt(&p, qtrue);
4797 			if(Q_stricmp(token, "("))
4798 			{
4799 				break;
4800 			}
4801 
4802 			while(1)
4803 			{
4804 				token = Com_ParseExt(&p, qtrue);
4805 
4806 				if(!token[0])
4807 					break;
4808 
4809 				if(!Q_stricmp(token, ")"))
4810 					break;
4811 			}
4812 
4813 			if(Q_stricmp(token, ")"))
4814 			{
4815 				break;
4816 			}
4817 		}
4818 		else
4819 		{
4820 			// skip the shader body
4821 			Com_SkipBracedSection(&p);
4822 		}
4823 	}
4824 
4825 	return NULL;
4826 }
4827 
4828 
4829 
4830 /*
4831 ==================
4832 R_FindShaderByName
4833 
4834 Will always return a valid shader, but it might be the
4835 default shader if the real one can't be found.
4836 ==================
4837 */
R_FindShaderByName(const char * name)4838 shader_t       *R_FindShaderByName(const char *name)
4839 {
4840 	char            strippedName[MAX_QPATH];
4841 	int             hash;
4842 	shader_t       *sh;
4843 
4844 	if((name == NULL) || (name[0] == 0))
4845 	{							// bk001205
4846 		return tr.defaultShader;
4847 	}
4848 
4849 	Com_StripExtension(name, strippedName, sizeof(strippedName));
4850 
4851 	hash = generateHashValue(strippedName, FILE_HASH_SIZE);
4852 
4853 	// see if the shader is already loaded
4854 	for(sh = shaderHashTable[hash]; sh; sh = sh->next)
4855 	{
4856 		// NOTE: if there was no shader or image available with the name strippedName
4857 		// then a default shader is created with type == SHADER_3D_DYNAMIC, so we
4858 		// have to check all default shaders otherwise for every call to R_FindShader
4859 		// with that same strippedName a new default shader is created.
4860 		if(Q_stricmp(sh->name, strippedName) == 0)
4861 		{
4862 			// match found
4863 			return sh;
4864 		}
4865 	}
4866 
4867 	return tr.defaultShader;
4868 }
4869 
4870 
4871 /*
4872 ===============
4873 R_FindShader
4874 
4875 Will always return a valid shader, but it might be the
4876 default shader if the real one can't be found.
4877 
4878 In the interest of not requiring an explicit shader text entry to
4879 be defined for every single image used in the game, three default
4880 shader behaviors can be auto-created for any image:
4881 
4882 If type == SHADER_2D, then the image will be used
4883 for 2D rendering unless an explicit shader is found
4884 
4885 If type == SHADER_3D_DYNAMIC, then the image will have
4886 dynamic diffuse lighting applied to it, as apropriate for most
4887 entity skin surfaces.
4888 
4889 If type == SHADER_3D_STATIC, then the image will use
4890 the vertex rgba modulate values, as apropriate for misc_model
4891 pre-lit surfaces.
4892 
4893 Other lightmapIndex values will have a lightmap stage created
4894 and src*dest blending applied with the texture, as apropriate for
4895 most world construction surfaces.
4896 
4897 ===============
4898 */
R_FindShader(const char * name,shaderType_t type,qboolean mipRawImage)4899 shader_t       *R_FindShader(const char *name, shaderType_t type, qboolean mipRawImage)
4900 {
4901 	char            strippedName[MAX_QPATH];
4902 	char            fileName[MAX_QPATH];
4903 	int             i, hash;
4904 	char           *shaderText;
4905 	image_t        *image;
4906 	shader_t       *sh;
4907 
4908 	if(name[0] == 0)
4909 	{
4910 		return tr.defaultShader;
4911 	}
4912 
4913 	Com_StripExtension(name, strippedName, sizeof(strippedName));
4914 
4915 	hash = generateHashValue(strippedName, FILE_HASH_SIZE);
4916 
4917 	// see if the shader is already loaded
4918 	for(sh = shaderHashTable[hash]; sh; sh = sh->next)
4919 	{
4920 		// NOTE: if there was no shader or image available with the name strippedName
4921 		// then a default shader is created with type == SHADER_3D_DYNAMIC, so we
4922 		// have to check all default shaders otherwise for every call to R_FindShader
4923 		// with that same strippedName a new default shader is created.
4924 		if((sh->type == type || sh->defaultShader) && !Q_stricmp(sh->name, strippedName))
4925 		{
4926 			// match found
4927 			return sh;
4928 		}
4929 	}
4930 
4931 	// make sure the render thread is stopped, because we are probably
4932 	// going to have to upload an image
4933 	if(r_smp->integer)
4934 	{
4935 		R_SyncRenderThread();
4936 	}
4937 
4938 	// clear the global shader
4939 	Com_Memset(&shader, 0, sizeof(shader));
4940 	Com_Memset(&stages, 0, sizeof(stages));
4941 	Q_strncpyz(shader.name, strippedName, sizeof(shader.name));
4942 	shader.type = type;
4943 	for(i = 0; i < MAX_SHADER_STAGES; i++)
4944 	{
4945 		stages[i].bundle[0].texMods = texMods[i];
4946 	}
4947 
4948 	// attempt to define shader from an explicit parameter file
4949 	shaderText = FindShaderInShaderText(strippedName);
4950 	if(shaderText)
4951 	{
4952 		if(!ParseShader(shaderText))
4953 		{
4954 			// had errors, so use default shader
4955 			shader.defaultShader = qtrue;
4956 		}
4957 		sh = FinishShader();
4958 		return sh;
4959 	}
4960 
4961 	// if not defined in the in-memory shader descriptions,
4962 	// look for a single TGA, BMP, or PCX
4963 	Q_strncpyz(fileName, name, sizeof(fileName));
4964 	Com_DefaultExtension(fileName, sizeof(fileName), ".tga");
4965 	image = R_FindImageFile(fileName,
4966 							mipRawImage ? IF_NONE : IF_NOPICMIP,
4967 							mipRawImage ? FT_DEFAULT : FT_LINEAR,
4968 							mipRawImage ? WT_REPEAT : WT_CLAMP);
4969 	if(!image)
4970 	{
4971 		ri.Printf(PRINT_DEVELOPER, "Couldn't find image file for shader %s\n", name);
4972 		shader.defaultShader = qtrue;
4973 		return FinishShader();
4974 	}
4975 
4976 	// create the default shading commands
4977 	switch (shader.type)
4978 	{
4979 		case SHADER_2D:
4980 		{
4981 			// GUI elements
4982 			stages[0].bundle[0].image[0] = image;
4983 			stages[0].active = qtrue;
4984 			stages[0].rgbGen = CGEN_VERTEX;
4985 			stages[0].alphaGen = AGEN_VERTEX;
4986 			stages[0].stateBits = GLS_DEPTHTEST_DISABLE |
4987 					GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
4988 			break;
4989 		}
4990 
4991 		case SHADER_3D_DYNAMIC:
4992 		{
4993 			// dynamic colors at vertexes
4994 			stages[0].type = ST_DIFFUSEMAP;
4995 			stages[0].bundle[0].image[0] = image;
4996 			stages[0].active = qtrue;
4997 			stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE;
4998 			stages[0].stateBits = GLS_DEFAULT;
4999 			break;
5000 		}
5001 
5002 		case SHADER_3D_STATIC:
5003 		{
5004 			// explicit colors at vertexes
5005 			stages[0].type = ST_DIFFUSEMAP;
5006 			stages[0].bundle[0].image[0] = image;
5007 			stages[0].active = qtrue;
5008 			stages[0].rgbGen = CGEN_EXACT_VERTEX;
5009 			stages[0].alphaGen = AGEN_SKIP;
5010 			stages[0].stateBits = GLS_DEFAULT;
5011 			break;
5012 		}
5013 
5014 		case SHADER_3D_LIGHTMAP:
5015 		{
5016 			// diffuseMap
5017 			stages[0].type = ST_DIFFUSEMAP;
5018 			stages[0].bundle[0].image[0] = image;
5019 			stages[0].active = qtrue;
5020 			stages[0].rgbGen = CGEN_IDENTITY;
5021 			//stages[0].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO;
5022 			stages[0].stateBits = GLS_DEFAULT;
5023 
5024 			// lightMap
5025 			stages[1].type = ST_LIGHTMAP;
5026 			stages[1].bundle[0].isLightMap = qtrue;
5027 			stages[1].active = qtrue;
5028 			stages[1].rgbGen = CGEN_IDENTITY;	// lightmaps are scaled on creation
5029 			// for identitylight
5030 			stages[1].stateBits = GLS_DEFAULT;
5031 			break;
5032 		}
5033 
5034 		case SHADER_LIGHT:
5035 		{
5036 			stages[0].type = ST_ATTENUATIONMAP_Z;
5037 			stages[0].bundle[0].image[0] = tr.noFalloffImage; // FIXME should be attenuationZImage
5038 			stages[0].active = qtrue;
5039 			stages[0].rgbGen = CGEN_IDENTITY;
5040 			stages[0].stateBits = GLS_DEFAULT;
5041 
5042 			stages[1].type = ST_ATTENUATIONMAP_XY;
5043 			stages[1].bundle[0].image[0] = image;
5044 			stages[1].active = qtrue;
5045 			stages[1].rgbGen = CGEN_IDENTITY;
5046 			stages[1].stateBits = GLS_DEFAULT;
5047 			//stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO;
5048 			break;
5049 		}
5050 
5051 		default:
5052 			break;
5053 	}
5054 
5055 	return FinishShader();
5056 }
5057 
5058 
RE_RegisterShaderFromImage(const char * name,image_t * image,qboolean mipRawImage)5059 qhandle_t RE_RegisterShaderFromImage(const char *name, image_t * image, qboolean mipRawImage)
5060 {
5061 	int             i, hash;
5062 	shader_t       *sh;
5063 
5064 	hash = generateHashValue(name, FILE_HASH_SIZE);
5065 
5066 	// see if the shader is already loaded
5067 	for(sh = shaderHashTable[hash]; sh; sh = sh->next)
5068 	{
5069 		// NOTE: if there was no shader or image available with the name strippedName
5070 		// then a default shader is created with type == SHADER_3D_DYNAMIC, so we
5071 		// have to check all default shaders otherwise for every call to R_FindShader
5072 		// with that same strippedName a new default shader is created.
5073 		if((sh->type == SHADER_2D || sh->defaultShader) && !Q_stricmp(sh->name, name))
5074 		{
5075 			// match found
5076 			return sh->index;
5077 		}
5078 	}
5079 
5080 	// make sure the render thread is stopped, because we are probably
5081 	// going to have to upload an image
5082 	if(r_smp->integer)
5083 	{
5084 		R_SyncRenderThread();
5085 	}
5086 
5087 	// clear the global shader
5088 	Com_Memset(&shader, 0, sizeof(shader));
5089 	Com_Memset(&stages, 0, sizeof(stages));
5090 	Q_strncpyz(shader.name, name, sizeof(shader.name));
5091 	shader.type = SHADER_2D;
5092 	for(i = 0; i < MAX_SHADER_STAGES; i++)
5093 	{
5094 		stages[i].bundle[0].texMods = texMods[i];
5095 	}
5096 
5097 	// create the default shading commands
5098 
5099 	// GUI elements
5100 	stages[0].bundle[0].image[0] = image;
5101 	stages[0].active = qtrue;
5102 	stages[0].rgbGen = CGEN_VERTEX;
5103 	stages[0].alphaGen = AGEN_VERTEX;
5104 	stages[0].stateBits = GLS_DEPTHTEST_DISABLE |
5105 		GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
5106 
5107 	sh = FinishShader();
5108 	return sh->index;
5109 }
5110 
5111 
5112 /*
5113 ====================
5114 RE_RegisterShader
5115 
5116 This is the exported shader entry point for the rest of the system
5117 It will always return an index that will be valid.
5118 
5119 This should really only be used for explicit shaders, because there is no
5120 way to ask for different implicit lighting modes (vertex, lightmap, etc)
5121 ====================
5122 */
RE_RegisterShaderLightMap(const char * name,int lightmapIndex)5123 qhandle_t RE_RegisterShaderLightMap(const char *name, int lightmapIndex)
5124 {
5125 	shader_t       *sh;
5126 
5127 	if(strlen(name) >= MAX_QPATH)
5128 	{
5129 		Com_Printf("Shader name exceeds MAX_QPATH\n");
5130 		return 0;
5131 	}
5132 
5133 	sh = R_FindShader(name, lightmapIndex, qtrue);
5134 
5135 	// we want to return 0 if the shader failed to
5136 	// load for some reason, but R_FindShader should
5137 	// still keep a name allocated for it, so if
5138 	// something calls RE_RegisterShader again with
5139 	// the same name, we don't try looking for it again
5140 	if(sh->defaultShader)
5141 	{
5142 		return 0;
5143 	}
5144 
5145 	return sh->index;
5146 }
5147 
5148 
5149 /*
5150 ====================
5151 RE_RegisterShader
5152 
5153 This is the exported shader entry point for the rest of the system
5154 It will always return an index that will be valid.
5155 
5156 This should really only be used for explicit shaders, because there is no
5157 way to ask for different implicit lighting modes (vertex, lightmap, etc)
5158 ====================
5159 */
RE_RegisterShader(const char * name)5160 qhandle_t RE_RegisterShader(const char *name)
5161 {
5162 	shader_t       *sh;
5163 
5164 	if(strlen(name) >= MAX_QPATH)
5165 	{
5166 		Com_Printf("Shader name exceeds MAX_QPATH\n");
5167 		return 0;
5168 	}
5169 
5170 	sh = R_FindShader(name, SHADER_2D, qtrue);
5171 
5172 	// we want to return 0 if the shader failed to
5173 	// load for some reason, but R_FindShader should
5174 	// still keep a name allocated for it, so if
5175 	// something calls RE_RegisterShader again with
5176 	// the same name, we don't try looking for it again
5177 	if(sh->defaultShader)
5178 	{
5179 		return 0;
5180 	}
5181 
5182 	return sh->index;
5183 }
5184 
5185 
5186 /*
5187 ====================
5188 RE_RegisterShaderNoMip
5189 
5190 For menu graphics that should never be picmiped
5191 ====================
5192 */
RE_RegisterShaderNoMip(const char * name)5193 qhandle_t RE_RegisterShaderNoMip(const char *name)
5194 {
5195 	shader_t       *sh;
5196 
5197 	if(strlen(name) >= MAX_QPATH)
5198 	{
5199 		Com_Printf("Shader name exceeds MAX_QPATH\n");
5200 		return 0;
5201 	}
5202 
5203 	sh = R_FindShader(name, SHADER_2D, qfalse);
5204 
5205 	// we want to return 0 if the shader failed to
5206 	// load for some reason, but R_FindShader should
5207 	// still keep a name allocated for it, so if
5208 	// something calls RE_RegisterShader again with
5209 	// the same name, we don't try looking for it again
5210 	if(sh->defaultShader)
5211 	{
5212 		return 0;
5213 	}
5214 
5215 	return sh->index;
5216 }
5217 
5218 /*
5219 ====================
5220 RE_RegisterShaderLightAttenuation
5221 
5222 For different Doom3 style light effects
5223 ====================
5224 */
RE_RegisterShaderLightAttenuation(const char * name)5225 qhandle_t RE_RegisterShaderLightAttenuation(const char *name)
5226 {
5227 	shader_t       *sh;
5228 
5229 	if(strlen(name) >= MAX_QPATH)
5230 	{
5231 		Com_Printf("Shader name exceeds MAX_QPATH\n");
5232 		return 0;
5233 	}
5234 
5235 	sh = R_FindShader(name, SHADER_LIGHT, qfalse);
5236 
5237 	// we want to return 0 if the shader failed to
5238 	// load for some reason, but R_FindShader should
5239 	// still keep a name allocated for it, so if
5240 	// something calls RE_RegisterShader again with
5241 	// the same name, we don't try looking for it again
5242 	if(sh->defaultShader)
5243 	{
5244 		return 0;
5245 	}
5246 
5247 	return sh->index;
5248 }
5249 
5250 
5251 /*
5252 ====================
5253 R_GetShaderByHandle
5254 
5255 When a handle is passed in by another module, this range checks
5256 it and returns a valid (possibly default) shader_t to be used internally.
5257 ====================
5258 */
R_GetShaderByHandle(qhandle_t hShader)5259 shader_t       *R_GetShaderByHandle(qhandle_t hShader)
5260 {
5261 	if(hShader < 0)
5262 	{
5263 		ri.Printf(PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader);	// bk: FIXME name
5264 		return tr.defaultShader;
5265 	}
5266 	if(hShader >= tr.numShaders)
5267 	{
5268 		ri.Printf(PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader);
5269 		return tr.defaultShader;
5270 	}
5271 	return tr.shaders[hShader];
5272 }
5273 
5274 /*
5275 ===============
5276 R_ShaderList_f
5277 
5278 Dump information on all valid shaders to the console
5279 A second parameter will cause it to print in sorted order
5280 ===============
5281 */
R_ShaderList_f(void)5282 void R_ShaderList_f(void)
5283 {
5284 	int             i;
5285 	int             count;
5286 	shader_t       *shader;
5287 	char           *s = NULL;
5288 
5289 	ri.Printf(PRINT_ALL, "-----------------------\n");
5290 
5291 	if(ri.Cmd_Argc() > 1)
5292 	{
5293 		s = ri.Cmd_Argv(1);
5294 	}
5295 
5296 	count = 0;
5297 	for(i = 0; i < tr.numShaders; i++)
5298 	{
5299 		if(ri.Cmd_Argc() > 2)
5300 		{
5301 			shader = tr.sortedShaders[i];
5302 		}
5303 		else
5304 		{
5305 			shader = tr.shaders[i];
5306 		}
5307 
5308 		if(s && Q_stricmpn(shader->name, s, strlen(s)) != 0)
5309 			continue;
5310 
5311 		ri.Printf(PRINT_ALL, "%i ", shader->numStages);
5312 
5313 		switch (shader->type)
5314 		{
5315 			case SHADER_2D:
5316 				ri.Printf(PRINT_ALL, "2D   ");
5317 				break;
5318 
5319 			case SHADER_3D_DYNAMIC:
5320 				ri.Printf(PRINT_ALL, "3D_D ");
5321 				break;
5322 
5323 			case SHADER_3D_STATIC:
5324 				ri.Printf(PRINT_ALL, "3D_S ");
5325 				break;
5326 
5327 			case SHADER_3D_LIGHTMAP:
5328 				ri.Printf(PRINT_ALL, "3D_L ");
5329 				break;
5330 
5331 			case SHADER_LIGHT:
5332 				ri.Printf(PRINT_ALL, "ATTN ");
5333 				break;
5334 		}
5335 
5336 		if(shader->collapseType == COLLAPSE_Generic_multi)
5337 		{
5338 			if(shader->collapseTextureEnv == GL_ADD)
5339 			{
5340 				ri.Printf(PRINT_ALL, "MT(a)          ");
5341 			}
5342 			else if(shader->collapseTextureEnv == GL_MODULATE)
5343 			{
5344 				ri.Printf(PRINT_ALL, "MT(m)          ");
5345 			}
5346 			else if(shader->collapseTextureEnv == GL_DECAL)
5347 			{
5348 				ri.Printf(PRINT_ALL, "MT(d)          ");
5349 			}
5350 		}
5351 		else if(shader->collapseType == COLLAPSE_lighting_D_radiosity)
5352 		{
5353 			ri.Printf(PRINT_ALL, "D_radiosity    ");
5354 		}
5355 		else if(shader->collapseType == COLLAPSE_lighting_DB_radiosity)
5356 		{
5357 			ri.Printf(PRINT_ALL, "DB_radiosity   ");
5358 		}
5359 		else if(shader->collapseType == COLLAPSE_lighting_DBS_radiosity)
5360 		{
5361 			ri.Printf(PRINT_ALL, "DBS_radiosity  ");
5362 		}
5363 		else if(shader->collapseType == COLLAPSE_lighting_DB_direct)
5364 		{
5365 			ri.Printf(PRINT_ALL, "DB_direct      ");
5366 		}
5367 		else if(shader->collapseType == COLLAPSE_lighting_DBS_direct)
5368 		{
5369 			ri.Printf(PRINT_ALL, "DBS_direct     ");
5370 		}
5371 		else if(shader->collapseType == COLLAPSE_lighting_DB_generic)
5372 		{
5373 			ri.Printf(PRINT_ALL, "DB_generic     ");
5374 		}
5375 		else if(shader->collapseType == COLLAPSE_lighting_DBS_generic)
5376 		{
5377 			ri.Printf(PRINT_ALL, "DBS_generic    ");
5378 		}
5379 		else
5380 		{
5381 			ri.Printf(PRINT_ALL, "               ");
5382 		}
5383 
5384 		if(shader->createdByGuide)
5385 		{
5386 			ri.Printf(PRINT_ALL, "G ");
5387 		}
5388 		else if(shader->explicitlyDefined)
5389 		{
5390 			ri.Printf(PRINT_ALL, "E ");
5391 		}
5392 		else
5393 		{
5394 			ri.Printf(PRINT_ALL, "  ");
5395 		}
5396 
5397 		if(shader->sort == SS_BAD)
5398 		{
5399 			ri.Printf(PRINT_ALL, "SS_BAD              ");
5400 		}
5401 		else if(shader->sort == SS_PORTAL)
5402 		{
5403 			ri.Printf(PRINT_ALL, "SS_PORTAL           ");
5404 		}
5405 		else if(shader->sort == SS_ENVIRONMENT)
5406 		{
5407 			ri.Printf(PRINT_ALL, "SS_ENVIRONMENT      ");
5408 		}
5409 		else if(shader->sort == SS_OPAQUE)
5410 		{
5411 			ri.Printf(PRINT_ALL, "SS_OPAQUE           ");
5412 		}
5413 		else if(shader->sort == SS_DECAL)
5414 		{
5415 			ri.Printf(PRINT_ALL, "SS_DECAL            ");
5416 		}
5417 		else if(shader->sort == SS_SEE_THROUGH)
5418 		{
5419 			ri.Printf(PRINT_ALL, "SS_SEE_THROUGH      ");
5420 		}
5421 		else if(shader->sort == SS_BANNER)
5422 		{
5423 			ri.Printf(PRINT_ALL, "SS_BANNER           ");
5424 		}
5425 		else if(shader->sort == SS_UNDERWATER)
5426 		{
5427 			ri.Printf(PRINT_ALL, "SS_UNDERWATER       ");
5428 		}
5429 		else if(shader->sort == SS_WATER)
5430 		{
5431 			ri.Printf(PRINT_ALL, "SS_WATER            ");
5432 		}
5433 		else if(shader->sort == SS_FAR)
5434 		{
5435 			ri.Printf(PRINT_ALL, "SS_FAR              ");
5436 		}
5437 		else if(shader->sort == SS_MEDIUM)
5438 		{
5439 			ri.Printf(PRINT_ALL, "SS_MEDIUM           ");
5440 		}
5441 		else if(shader->sort == SS_CLOSE)
5442 		{
5443 			ri.Printf(PRINT_ALL, "SS_CLOSE            ");
5444 		}
5445 		else if(shader->sort == SS_BLEND0)
5446 		{
5447 			ri.Printf(PRINT_ALL, "SS_BLEND0           ");
5448 		}
5449 		else if(shader->sort == SS_BLEND1)
5450 		{
5451 			ri.Printf(PRINT_ALL, "SS_BLEND1           ");
5452 		}
5453 		else if(shader->sort == SS_BLEND2)
5454 		{
5455 			ri.Printf(PRINT_ALL, "SS_BLEND2           ");
5456 		}
5457 		else if(shader->sort == SS_BLEND3)
5458 		{
5459 			ri.Printf(PRINT_ALL, "SS_BLEND3           ");
5460 		}
5461 		else if(shader->sort == SS_BLEND6)
5462 		{
5463 			ri.Printf(PRINT_ALL, "SS_BLEND6           ");
5464 		}
5465 		else if(shader->sort == SS_ALMOST_NEAREST)
5466 		{
5467 			ri.Printf(PRINT_ALL, "SS_ALMOST_NEAREST   ");
5468 		}
5469 		else if(shader->sort == SS_NEAREST)
5470 		{
5471 			ri.Printf(PRINT_ALL, "SS_NEAREST          ");
5472 		}
5473 		else if(shader->sort == SS_POST_PROCESS)
5474 		{
5475 			ri.Printf(PRINT_ALL, "SS_POST_PROCESS     ");
5476 		}
5477 		else
5478 		{
5479 			ri.Printf(PRINT_ALL, "                    ");
5480 		}
5481 
5482 		if(shader->defaultShader)
5483 		{
5484 			ri.Printf(PRINT_ALL, ": %s (DEFAULTED)\n", shader->name);
5485 		}
5486 		else
5487 		{
5488 			ri.Printf(PRINT_ALL, ": %s\n", shader->name);
5489 		}
5490 		count++;
5491 	}
5492 	ri.Printf(PRINT_ALL, "%i total shaders\n", count);
5493 	ri.Printf(PRINT_ALL, "------------------\n");
5494 }
5495 
R_ShaderExp_f(void)5496 void R_ShaderExp_f(void)
5497 {
5498 	int             i;
5499 	int				len;
5500 	char			buffer[1024] = "";
5501 	char           *buffer_p = &buffer[0];
5502 	expression_t	exp;
5503 
5504 	strcpy(shader.name, "dummy");
5505 
5506 	ri.Printf(PRINT_ALL, "-----------------------\n");
5507 
5508 	for(i = 1; i < ri.Cmd_Argc(); i++)
5509 	{
5510 		strcat(buffer, ri.Cmd_Argv(i));
5511 		strcat(buffer, " ");
5512 	}
5513 	len = strlen(buffer);
5514 	buffer[len -1] = 0;	// replace last " " with tailing zero
5515 
5516 	ParseExpression(&buffer_p, &exp);
5517 
5518 	ri.Printf(PRINT_ALL, "%i total ops\n", exp.numOps);
5519 	ri.Printf(PRINT_ALL, "%f result\n", RB_EvalExpression(&exp, 0));
5520 	ri.Printf(PRINT_ALL, "------------------\n");
5521 }
5522 
5523 /*
5524 ====================
5525 ScanAndLoadShaderGuides
5526 
5527 Finds and loads all .guide files, combining them into
5528 a single large text block that can be scanned for shader template names
5529 =====================
5530 */
5531 #define	MAX_GUIDE_FILES	1024
ScanAndLoadGuideFiles(void)5532 static void ScanAndLoadGuideFiles(void)
5533 {
5534 	char          **guideFiles;
5535 	char           *buffers[MAX_GUIDE_FILES];
5536 	char           *p;
5537 	int             numGuides;
5538 	int             i;
5539 	char           *oldp, *token, *hashMem;
5540 	int             guideTextHashTableSizes[MAX_GUIDETEXT_HASH], hash, size;
5541 	char            filename[MAX_QPATH];
5542 	long            sum = 0;
5543 
5544 	ri.Printf(PRINT_ALL, "----- ScanAndLoadGuideFiles -----\n");
5545 
5546 	// scan for guide files
5547 	guideFiles = ri.FS_ListFiles("guides", ".guide", &numGuides);
5548 
5549 	if(!guideFiles || !numGuides)
5550 	{
5551 		ri.Printf(PRINT_WARNING, "WARNING: no shader guide files found\n");
5552 		return;
5553 	}
5554 
5555 	if(numGuides > MAX_GUIDE_FILES)
5556 	{
5557 		numGuides = MAX_GUIDE_FILES;
5558 	}
5559 
5560 	// build single large buffer
5561 	for(i = 0; i < numGuides; i++)
5562 	{
5563 		Com_sprintf(filename, sizeof(filename), "guides/%s", guideFiles[i]);
5564 
5565 		sum += ri.FS_ReadFile(filename, NULL);
5566 	}
5567 	s_guideText = ri.Hunk_Alloc(sum + numGuides * 2, h_low);
5568 
5569 	// load in reverse order, so doubled templates are overriden properly
5570 	for(i = numGuides - 1; i >= 0; i--)
5571 	{
5572 		Com_sprintf(filename, sizeof(filename), "guides/%s", guideFiles[i]);
5573 
5574 		ri.Printf(PRINT_DEVELOPER, "...loading '%s'\n", filename);
5575 		sum += ri.FS_ReadFile(filename, (void **)&buffers[i]);
5576 		if(!buffers[i])
5577 		{
5578 			ri.Error(ERR_DROP, "Couldn't load %s", filename);
5579 		}
5580 
5581 		strcat(s_guideText, "\n");
5582 		p = &s_guideText[strlen(s_guideText)];
5583 		strcat(s_guideText, buffers[i]);
5584 		ri.FS_FreeFile(buffers[i]);
5585 		buffers[i] = p;
5586 		Com_Compress(p);
5587 	}
5588 
5589 	Com_Memset(guideTextHashTableSizes, 0, sizeof(guideTextHashTableSizes));
5590 	size = 0;
5591 	//
5592 	for(i = 0; i < numGuides; i++)
5593 	{
5594 		Com_sprintf(filename, sizeof(filename), "guides/%s", guideFiles[i]);
5595 
5596 		Com_BeginParseSession(filename);
5597 
5598 		// pointer to the first shader file
5599 		p = buffers[i];
5600 
5601 		// look for label
5602 		while(1)
5603 		{
5604 			token = Com_ParseExt(&p, qtrue);
5605 			if(token[0] == 0)
5606 			{
5607 				break;
5608 			}
5609 
5610 			if(Q_stricmp(token, "guide") && Q_stricmp(token, "inlineGuide"))
5611 			{
5612 				Com_ParseWarning("expected guide or inlineGuide found '%s'\n", token);
5613 				break;
5614 			}
5615 
5616 			// parse guide name
5617 			token = Com_ParseExt(&p, qtrue);
5618 
5619 			//ri.Printf(PRINT_ALL, "guide: '%s'\n", token);
5620 
5621 			hash = generateHashValue(token, MAX_GUIDETEXT_HASH);
5622 			guideTextHashTableSizes[hash]++;
5623 			size++;
5624 
5625 			// skip parameters
5626 			token = Com_ParseExt(&p, qtrue);
5627 			if(Q_stricmp(token, "("))
5628 			{
5629 				Com_ParseWarning("expected ( found '%s'\n", token);
5630 				break;
5631 			}
5632 
5633 			while(1)
5634 			{
5635 				token = Com_ParseExt(&p, qtrue);
5636 
5637 				if(!token[0])
5638 					break;
5639 
5640 				if(!Q_stricmp(token, ")"))
5641 					break;
5642 			}
5643 
5644 			if(Q_stricmp(token, ")"))
5645 			{
5646 				Com_ParseWarning("expected ) found '%s'\n", token);
5647 				break;
5648 			}
5649 
5650 			// skip guide body
5651 			Com_SkipBracedSection(&p);
5652 
5653 			// if we passed the pointer to the next shader file
5654 			if(i < numGuides - 1)
5655 			{
5656 				if(p > buffers[i + 1])
5657 				{
5658 					break;
5659 				}
5660 			}
5661 		}
5662 	}
5663 
5664 	size += MAX_GUIDETEXT_HASH;
5665 
5666 	hashMem = ri.Hunk_Alloc(size * sizeof(char *), h_low);
5667 
5668 	for(i = 0; i < MAX_GUIDETEXT_HASH; i++)
5669 	{
5670 		guideTextHashTable[i] = (char **)hashMem;
5671 		hashMem = ((char *)hashMem) + ((guideTextHashTableSizes[i] + 1) * sizeof(char *));
5672 	}
5673 
5674 	Com_Memset(guideTextHashTableSizes, 0, sizeof(guideTextHashTableSizes));
5675 	//
5676 	for(i = 0; i < numGuides; i++)
5677 	{
5678 		Com_sprintf(filename, sizeof(filename), "guides/%s", guideFiles[i]);
5679 
5680 		Com_BeginParseSession(filename);
5681 
5682 		// pointer to the first shader file
5683 		p = buffers[i];
5684 
5685 		// look for label
5686 		while(1)
5687 		{
5688 			token = Com_ParseExt(&p, qtrue);
5689 			if(token[0] == 0)
5690 			{
5691 				break;
5692 			}
5693 
5694 			if(Q_stricmp(token, "guide") && Q_stricmp(token, "inlineGuide"))
5695 			{
5696 				Com_ParseWarning("expected guide or inlineGuide found '%s'\n", token);
5697 				break;
5698 			}
5699 
5700 			// parse guide name
5701 			oldp = p;
5702 			token = Com_ParseExt(&p, qtrue);
5703 
5704 			//ri.Printf(PRINT_ALL, "...hashing guide '%s'\n", token);
5705 
5706 			hash = generateHashValue(token, MAX_GUIDETEXT_HASH);
5707 			guideTextHashTable[hash][guideTextHashTableSizes[hash]++] = oldp;
5708 
5709 			// skip parameters
5710 			token = Com_ParseExt(&p, qtrue);
5711 			if(Q_stricmp(token, "("))
5712 			{
5713 				Com_ParseWarning("expected ( found '%s'\n", token);
5714 				break;
5715 			}
5716 
5717 			while(1)
5718 			{
5719 				token = Com_ParseExt(&p, qtrue);
5720 
5721 				if(!token[0])
5722 					break;
5723 
5724 				if(!Q_stricmp(token, ")"))
5725 					break;
5726 			}
5727 
5728 			if(Q_stricmp(token, ")"))
5729 			{
5730 				Com_ParseWarning("expected ) found '%s'\n", token);
5731 				break;
5732 			}
5733 
5734 			// skip guide body
5735 			Com_SkipBracedSection(&p);
5736 
5737 			// if we passed the pointer to the next shader file
5738 			if(i < numGuides - 1)
5739 			{
5740 				if(p > buffers[i + 1])
5741 				{
5742 					break;
5743 				}
5744 			}
5745 		}
5746 	}
5747 
5748 	// free up memory
5749 	ri.FS_FreeFileList(guideFiles);
5750 }
5751 
5752 /*
5753 ====================
5754 ScanAndLoadShaderFiles
5755 
5756 Finds and loads all .shader files, combining them into
5757 a single large text block that can be scanned for shader names
5758 =====================
5759 */
5760 #define	MAX_SHADER_FILES	4096
ScanAndLoadShaderFiles(void)5761 static void ScanAndLoadShaderFiles(void)
5762 {
5763 	char          **shaderFiles;
5764 	char           *buffers[MAX_SHADER_FILES];
5765 	char           *p;
5766 	int             numShaders;
5767 	int             i;
5768 	char           *oldp, *token, *hashMem;
5769 	int             shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size;
5770 	char            filename[MAX_QPATH];
5771 	long            sum = 0;
5772 
5773 	// scan for shader files
5774 	shaderFiles = ri.FS_ListFiles("scripts", ".shader", &numShaders);
5775 	if(!shaderFiles || !numShaders)
5776 	{
5777 		ri.Printf(PRINT_WARNING, "WARNING: no shader files found\n");
5778 		return;
5779 	}
5780 
5781 	if(numShaders > MAX_SHADER_FILES)
5782 	{
5783 		numShaders = MAX_SHADER_FILES;
5784 	}
5785 
5786 	// build single large buffer
5787 	for(i = 0; i < numShaders; i++)
5788 	{
5789 		Com_sprintf(filename, sizeof(filename), "scripts/%s", shaderFiles[i]);
5790 		sum += ri.FS_ReadFile(filename, NULL);
5791 	}
5792 	s_shaderText = ri.Hunk_Alloc(sum + numShaders * 2, h_low);
5793 
5794 	// load in reverse order, so doubled shaders are overriden properly
5795 	for(i = numShaders - 1; i >= 0; i--)
5796 	{
5797 		Com_sprintf(filename, sizeof(filename), "scripts/%s", shaderFiles[i]);
5798 
5799 		ri.Printf(PRINT_DEVELOPER, "...loading '%s'\n", filename);
5800 		sum += ri.FS_ReadFile(filename, (void **)&buffers[i]);
5801 		if(!buffers[i])
5802 		{
5803 			ri.Error(ERR_DROP, "Couldn't load %s", filename);
5804 		}
5805 
5806 		strcat(s_shaderText, "\n");
5807 		p = &s_shaderText[strlen(s_shaderText)];
5808 		strcat(s_shaderText, buffers[i]);
5809 		ri.FS_FreeFile(buffers[i]);
5810 		buffers[i] = p;
5811 		Com_Compress(p);
5812 	}
5813 
5814 	Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes));
5815 	size = 0;
5816 	for(i = 0; i < numShaders; i++)
5817 	{
5818 		Com_sprintf(filename, sizeof(filename), "scripts/%s", shaderFiles[i]);
5819 		Com_BeginParseSession(filename);
5820 
5821 		// pointer to the first shader file
5822 		p = buffers[i];
5823 
5824 		// look for label
5825 		while(1)
5826 		{
5827 			token = Com_ParseExt(&p, qtrue);
5828 			if(token[0] == 0)
5829 			{
5830 				break;
5831 			}
5832 
5833 			// skip shader tables
5834 			if(!Q_stricmp(token, "table"))
5835 			{
5836 				// skip table name
5837 				token = Com_ParseExt(&p, qtrue);
5838 
5839 				Com_SkipBracedSection(&p);
5840 			}
5841 			// support shader templates
5842 			else if(!Q_stricmp(token, "guide"))
5843 			{
5844 				// parse shader name
5845 				token = Com_ParseExt(&p, qtrue);
5846 
5847 				//ri.Printf(PRINT_ALL, "...guided '%s'\n", token);
5848 
5849 				hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
5850 				shaderTextHashTableSizes[hash]++;
5851 				size++;
5852 
5853 				// skip guide name
5854 				token = Com_ParseExt(&p, qtrue);
5855 
5856 				// skip parameters
5857 				token = Com_ParseExt(&p, qtrue);
5858 				if(Q_stricmp(token, "("))
5859 				{
5860 					Com_ParseWarning("expected ( found '%s'\n", token);
5861 					break;
5862 				}
5863 
5864 				while(1)
5865 				{
5866 					token = Com_ParseExt(&p, qtrue);
5867 
5868 					if(!token[0])
5869 						break;
5870 
5871 					if(!Q_stricmp(token, ")"))
5872 						break;
5873 				}
5874 
5875 				if(Q_stricmp(token, ")"))
5876 				{
5877 					Com_ParseWarning("expected ) found '%s'\n", token);
5878 					break;
5879 				}
5880 			}
5881 			else
5882 			{
5883 				hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
5884 				shaderTextHashTableSizes[hash]++;
5885 				size++;
5886 
5887 				Com_SkipBracedSection(&p);
5888 			}
5889 
5890 			// if we passed the pointer to the next shader file
5891 			if(i < numShaders - 1)
5892 			{
5893 				if(p > buffers[i + 1])
5894 				{
5895 					break;
5896 				}
5897 			}
5898 		}
5899 	}
5900 
5901 	size += MAX_SHADERTEXT_HASH;
5902 
5903 	hashMem = ri.Hunk_Alloc(size * sizeof(char *), h_low);
5904 
5905 	for(i = 0; i < MAX_SHADERTEXT_HASH; i++)
5906 	{
5907 		shaderTextHashTable[i] = (char **)hashMem;
5908 		hashMem = ((char *)hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *));
5909 	}
5910 
5911 	Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes));
5912 
5913 	for(i = 0; i < numShaders; i++)
5914 	{
5915 		Com_sprintf(filename, sizeof(filename), "scripts/%s", shaderFiles[i]);
5916 		Com_BeginParseSession(filename);
5917 
5918 		// pointer to the first shader file
5919 		p = buffers[i];
5920 
5921 		// look for label
5922 		while(1)
5923 		{
5924 			oldp = p;
5925 			token = Com_ParseExt(&p, qtrue);
5926 			if(token[0] == 0)
5927 			{
5928 				break;
5929 			}
5930 
5931 			// parse shader tables
5932 			if(!Q_stricmp(token, "table"))
5933 			{
5934 				int             depth;
5935 				float			values[FUNCTABLE_SIZE];
5936 				int				numValues;
5937 				shaderTable_t  *tb;
5938 				qboolean        alreadyCreated;
5939 
5940 				Com_Memset(&table, 0, sizeof(table));
5941 
5942 				token = Com_ParseExt(&p, qtrue);
5943 				Q_strncpyz(table.name, token, sizeof(table.name));
5944 
5945 				// check if already created
5946 				alreadyCreated = qfalse;
5947 				hash = generateHashValue(table.name, MAX_SHADERTABLE_HASH);
5948 				for(tb = shaderTableHashTable[hash]; tb; tb = tb->next)
5949 				{
5950 					if(Q_stricmp(tb->name, table.name) == 0)
5951 					{
5952 						// match found
5953 						alreadyCreated = qtrue;
5954 						break;
5955 					}
5956 				}
5957 
5958 				depth = 0;
5959 				numValues = 0;
5960 				do
5961 				{
5962 					token = Com_ParseExt(&p, qtrue);
5963 
5964 					if(!Q_stricmp(token, "snap"))
5965 					{
5966 						table.snap = qtrue;
5967 					}
5968 					else if(!Q_stricmp(token, "clamp"))
5969 					{
5970 						table.clamp = qtrue;
5971 					}
5972 					else if(token[0] == '{')
5973 					{
5974 						depth++;
5975 					}
5976 					else if(token[0] == '}')
5977 					{
5978 						depth--;
5979 					}
5980 					else if(token[0] == ',')
5981 					{
5982 						continue;
5983 					}
5984 					else
5985 					{
5986 						if(numValues == FUNCTABLE_SIZE)
5987 						{
5988 							ri.Printf(PRINT_WARNING, "WARNING: FUNCTABLE_SIZE hit\n");
5989 							break;
5990 						}
5991 						values[numValues++] = atof(token);
5992 					}
5993 				} while(depth && p);
5994 
5995 				if(!alreadyCreated)
5996 				{
5997 					ri.Printf(PRINT_DEVELOPER, "...generating '%s'\n", table.name);
5998 					GeneratePermanentShaderTable(values, numValues);
5999 				}
6000 			}
6001 			// support shader templates
6002 			else if(!Q_stricmp(token, "guide"))
6003 			{
6004 				// parse shader name
6005 				oldp = p;
6006 				token = Com_ParseExt(&p, qtrue);
6007 
6008 				//ri.Printf(PRINT_ALL, "...guided '%s'\n", token);
6009 
6010 				hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
6011 				shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp;
6012 
6013 				// skip guide name
6014 				token = Com_ParseExt(&p, qtrue);
6015 
6016 				// skip parameters
6017 				token = Com_ParseExt(&p, qtrue);
6018 				if(Q_stricmp(token, "("))
6019 				{
6020 					Com_ParseWarning("expected ( found '%s'\n", token);
6021 					break;
6022 				}
6023 
6024 				while(1)
6025 				{
6026 					token = Com_ParseExt(&p, qtrue);
6027 
6028 					if(!token[0])
6029 						break;
6030 
6031 					if(!Q_stricmp(token, ")"))
6032 						break;
6033 				}
6034 
6035 				if(Q_stricmp(token, ")"))
6036 				{
6037 					Com_ParseWarning("expected ) found '%s'\n", token);
6038 					break;
6039 				}
6040 			}
6041 			else
6042 			{
6043 				hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
6044 				shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp;
6045 
6046 				// skip shaderbody
6047 				Com_SkipBracedSection(&p);
6048 			}
6049 
6050 			// if we passed the pointer to the next shader file
6051 			if(i < numShaders - 1)
6052 			{
6053 				if(p > buffers[i + 1])
6054 				{
6055 					break;
6056 				}
6057 			}
6058 		}
6059 	}
6060 
6061 	// free up memory
6062 	ri.FS_FreeFileList(shaderFiles);
6063 }
6064 
6065 
6066 /*
6067 ====================
6068 CreateInternalShaders
6069 ====================
6070 */
CreateInternalShaders(void)6071 static void CreateInternalShaders(void)
6072 {
6073 	tr.numShaders = 0;
6074 
6075 	// init the default shader
6076 	Com_Memset(&shader, 0, sizeof(shader));
6077 	Com_Memset(&stages, 0, sizeof(stages));
6078 
6079 	Q_strncpyz(shader.name, "<default>", sizeof(shader.name));
6080 
6081 	shader.type = SHADER_3D_DYNAMIC;
6082 	stages[0].bundle[0].image[0] = tr.defaultImage;
6083 	stages[0].active = qtrue;
6084 	stages[0].stateBits = GLS_DEFAULT;
6085 	tr.defaultShader = FinishShader();
6086 }
6087 
CreateExternalShaders(void)6088 static void CreateExternalShaders(void)
6089 {
6090 	tr.projectionShadowShader = R_FindShader("projectionShadow", SHADER_3D_DYNAMIC, qtrue);
6091 
6092 	tr.flareShader = R_FindShader("flareShader", SHADER_3D_DYNAMIC, qtrue);
6093 	tr.sunShader = R_FindShader("sun", SHADER_3D_DYNAMIC, qtrue);
6094 
6095 	tr.defaultPointLightShader = R_FindShader("lights/defaultPointLight", SHADER_LIGHT, qtrue);
6096 	tr.defaultProjectedLightShader = R_FindShader("lights/defaultProjectedLight", SHADER_LIGHT, qtrue);
6097 	tr.defaultDynamicLightShader = R_FindShader("lights/defaultDynamicLight", SHADER_LIGHT, qtrue);
6098 }
6099 
6100 /*
6101 ==================
6102 R_InitShaders
6103 ==================
6104 */
R_InitShaders(void)6105 void R_InitShaders(void)
6106 {
6107 	ri.Printf( PRINT_ALL, "Initializing Shaders\n" );
6108 
6109 	Com_Memset(shaderTableHashTable, 0, sizeof(shaderTableHashTable));
6110 	Com_Memset(shaderHashTable, 0, sizeof(shaderHashTable));
6111 
6112 	deferLoad = qfalse;
6113 
6114 	CreateInternalShaders();
6115 
6116 	ScanAndLoadGuideFiles();
6117 
6118 	ScanAndLoadShaderFiles();
6119 
6120 	CreateExternalShaders();
6121 }
6122