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