]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Material.cpp
hello world
[icculus/iodoom3.git] / neo / renderer / Material.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "tr_local.h"
33
34 /*
35
36 Any errors during parsing just set MF_DEFAULTED and return, rather than throwing
37 a hard error. This will cause the material to fall back to default material,
38 but otherwise let things continue.
39
40 Each material may have a set of calculations that must be evaluated before
41 drawing with it.
42
43 Every expression that a material uses can be evaluated at one time, which
44 will allow for perfect common subexpression removal when I get around to
45 writing it.
46
47 Without this, scrolling an entire surface could result in evaluating the
48 same texture matrix calculations a half dozen times.
49
50   Open question: should I allow arbitrary per-vertex color, texCoord, and vertex
51   calculations to be specified in the material code?
52
53   Every stage will definately have a valid image pointer.
54
55   We might want the ability to change the sort value based on conditionals,
56   but it could be a hassle to implement,
57
58 */
59
60 // keep all of these on the stack, when they are static it makes material parsing non-reentrant
61 typedef struct mtrParsingData_s {
62         bool                    registerIsTemporary[MAX_EXPRESSION_REGISTERS];
63         float                   shaderRegisters[MAX_EXPRESSION_REGISTERS];
64         expOp_t                 shaderOps[MAX_EXPRESSION_OPS];
65         shaderStage_t   parseStages[MAX_SHADER_STAGES];
66
67         bool                    registersAreConstant;
68         bool                    forceOverlays;
69 } mtrParsingData_t;
70
71
72 /*
73 =============
74 idMaterial::CommonInit
75 =============
76 */
77 void idMaterial::CommonInit() {
78         desc = "<none>";
79         renderBump = "";
80         contentFlags = CONTENTS_SOLID;
81         surfaceFlags = SURFTYPE_NONE;
82         materialFlags = 0;
83         sort = SS_BAD;
84         coverage = MC_BAD;
85         cullType = CT_FRONT_SIDED;
86         deform = DFRM_NONE;
87         numOps = 0;
88         ops = NULL;
89         numRegisters = 0;
90         expressionRegisters = NULL;
91         constantRegisters = NULL;
92         numStages = 0;
93         numAmbientStages = 0;
94         stages = NULL;
95         editorImage = NULL;
96         lightFalloffImage = NULL;
97         shouldCreateBackSides = false;
98         entityGui = 0;
99         fogLight = false;
100         blendLight = false;
101         ambientLight = false;
102         noFog = false;
103         hasSubview = false;
104         allowOverlays = true;
105         unsmoothedTangents = false;
106         gui = NULL;
107         memset( deformRegisters, 0, sizeof( deformRegisters ) );
108         editorAlpha = 1.0;
109         spectrum = 0;
110         polygonOffset = 0;
111         suppressInSubview = false;
112         refCount = 0;
113         portalSky = false;
114
115         decalInfo.stayTime = 10000;
116         decalInfo.fadeTime = 4000;
117         decalInfo.start[0] = 1;
118         decalInfo.start[1] = 1;
119         decalInfo.start[2] = 1;
120         decalInfo.start[3] = 1;
121         decalInfo.end[0] = 0;
122         decalInfo.end[1] = 0;
123         decalInfo.end[2] = 0;
124         decalInfo.end[3] = 0;
125 }
126
127 /*
128 =============
129 idMaterial::idMaterial
130 =============
131 */
132 idMaterial::idMaterial() {
133         CommonInit();
134
135         // we put this here instead of in CommonInit, because
136         // we don't want it cleared when a material is purged
137         surfaceArea = 0;
138 }
139
140 /*
141 =============
142 idMaterial::~idMaterial
143 =============
144 */
145 idMaterial::~idMaterial() {
146 }
147
148 /*
149 ===============
150 idMaterial::FreeData
151 ===============
152 */
153 void idMaterial::FreeData() {
154         int i;
155
156         if ( stages ) {
157                 // delete any idCinematic textures
158                 for ( i = 0; i < numStages; i++ ) {
159                         if ( stages[i].texture.cinematic != NULL ) {
160                                 delete stages[i].texture.cinematic;
161                                 stages[i].texture.cinematic = NULL;
162                         }
163                         if ( stages[i].newStage != NULL ) {
164                                 Mem_Free( stages[i].newStage );
165                                 stages[i].newStage = NULL;
166                         }
167                 }
168                 R_StaticFree( stages );
169                 stages = NULL;
170         }
171         if ( expressionRegisters != NULL ) {
172                 R_StaticFree( expressionRegisters );
173                 expressionRegisters = NULL;
174         }
175         if ( constantRegisters != NULL ) {
176                 R_StaticFree( constantRegisters );
177                 constantRegisters = NULL;
178         }
179         if ( ops != NULL ) {
180                 R_StaticFree( ops );
181                 ops = NULL;
182         }
183 }
184
185 /*
186 ==============
187 idMaterial::GetEditorImage
188 ==============
189 */
190 idImage *idMaterial::GetEditorImage( void ) const {
191         if ( editorImage ) {
192                 return editorImage;
193         }
194
195         // if we don't have an editorImageName, use the first stage image
196         if ( !editorImageName.Length()) {
197                 // _D3XP :: First check for a diffuse image, then use the first
198                 if ( numStages && stages ) {
199                         int i;
200                         for( i = 0; i < numStages; i++ ) {
201                                 if ( stages[i].lighting == SL_DIFFUSE ) {
202                                         editorImage = stages[i].texture.image;
203                                         break;
204                                 }
205                         }
206                         if ( !editorImage ) {
207                                 editorImage = stages[0].texture.image;
208                         }
209                 } else {
210                         editorImage = globalImages->defaultImage;
211                 }
212         } else {
213                 // look for an explicit one
214                 editorImage = globalImages->ImageFromFile( editorImageName, TF_DEFAULT, true, TR_REPEAT, TD_DEFAULT );
215         }
216
217         if ( !editorImage ) {
218                 editorImage = globalImages->defaultImage;
219         }
220
221         return editorImage;
222 }
223
224
225 // info parms
226 typedef struct {
227         char    *name;
228         int             clearSolid, surfaceFlags, contents;
229 } infoParm_t;
230
231 static infoParm_t       infoParms[] = {
232         // game relevant attributes
233         {"solid",               0,      0,      CONTENTS_SOLID },               // may need to override a clearSolid
234         {"water",               1,      0,      CONTENTS_WATER },               // used for water
235         {"playerclip",  0,      0,      CONTENTS_PLAYERCLIP },  // solid to players
236         {"monsterclip", 0,      0,      CONTENTS_MONSTERCLIP }, // solid to monsters
237         {"moveableclip",0,      0,      CONTENTS_MOVEABLECLIP },// solid to moveable entities
238         {"ikclip",              0,      0,      CONTENTS_IKCLIP },              // solid to IK
239         {"blood",               0,      0,      CONTENTS_BLOOD },               // used to detect blood decals
240         {"trigger",             0,      0,      CONTENTS_TRIGGER },             // used for triggers
241         {"aassolid",    0,      0,      CONTENTS_AAS_SOLID },   // solid for AAS
242         {"aasobstacle", 0,      0,      CONTENTS_AAS_OBSTACLE },// used to compile an obstacle into AAS that can be enabled/disabled
243         {"flashlight_trigger",  0,      0,      CONTENTS_FLASHLIGHT_TRIGGER }, // used for triggers that are activated by the flashlight
244         {"nonsolid",    1,      0,      0 },                                    // clears the solid flag
245         {"nullNormal",  0,      SURF_NULLNORMAL,0 },            // renderbump will draw as 0x80 0x80 0x80
246
247         // utility relevant attributes
248         {"areaportal",  1,      0,      CONTENTS_AREAPORTAL },  // divides areas
249         {"qer_nocarve", 1,      0,      CONTENTS_NOCSG},                // don't cut brushes in editor
250
251         {"discrete",    1,      SURF_DISCRETE,  0 },            // surfaces should not be automatically merged together or
252                                                                                                         // clipped to the world,
253                                                                                                         // because they represent discrete objects like gui shaders
254                                                                                                         // mirrors, or autosprites
255         {"noFragment",  0,      SURF_NOFRAGMENT,        0 },
256
257         {"slick",               0,      SURF_SLICK,             0 },
258         {"collision",   0,      SURF_COLLISION, 0 },
259         {"noimpact",    0,      SURF_NOIMPACT,  0 },            // don't make impact explosions or marks
260         {"nodamage",    0,      SURF_NODAMAGE,  0 },            // no falling damage when hitting
261         {"ladder",              0,      SURF_LADDER,    0 },            // climbable
262         {"nosteps",             0,      SURF_NOSTEPS,   0 },            // no footsteps
263
264         // material types for particle, sound, footstep feedback
265         {"metal",               0,  SURFTYPE_METAL,             0 },    // metal
266         {"stone",               0,  SURFTYPE_STONE,             0 },    // stone
267         {"flesh",               0,  SURFTYPE_FLESH,             0 },    // flesh
268         {"wood",                0,  SURFTYPE_WOOD,              0 },    // wood
269         {"cardboard",   0,      SURFTYPE_CARDBOARD,     0 },    // cardboard
270         {"liquid",              0,      SURFTYPE_LIQUID,        0 },    // liquid
271         {"glass",               0,      SURFTYPE_GLASS,         0 },    // glass
272         {"plastic",             0,      SURFTYPE_PLASTIC,       0 },    // plastic
273         {"ricochet",    0,      SURFTYPE_RICOCHET,      0 },    // behaves like metal but causes a ricochet sound
274
275         // unassigned surface types
276         {"surftype10",  0,      SURFTYPE_10,    0 },
277         {"surftype11",  0,      SURFTYPE_11,    0 },
278         {"surftype12",  0,      SURFTYPE_12,    0 },
279         {"surftype13",  0,      SURFTYPE_13,    0 },
280         {"surftype14",  0,      SURFTYPE_14,    0 },
281         {"surftype15",  0,      SURFTYPE_15,    0 },
282 };
283
284 static const int numInfoParms = sizeof(infoParms) / sizeof (infoParms[0]);
285
286
287 /*
288 ===============
289 idMaterial::CheckSurfaceParm
290
291 See if the current token matches one of the surface parm bit flags
292 ===============
293 */
294 bool idMaterial::CheckSurfaceParm( idToken *token ) {
295
296         for ( int i = 0 ; i < numInfoParms ; i++ ) {
297                 if ( !token->Icmp( infoParms[i].name ) ) {
298                         if ( infoParms[i].surfaceFlags & SURF_TYPE_MASK ) {
299                                 // ensure we only have one surface type set
300                                 surfaceFlags &= ~SURF_TYPE_MASK;
301                         }
302                         surfaceFlags |= infoParms[i].surfaceFlags;
303                         contentFlags |= infoParms[i].contents;
304                         if ( infoParms[i].clearSolid ) {
305                                 contentFlags &= ~CONTENTS_SOLID;
306                         }
307                         return true;
308                 }
309         }
310         return false;
311 }
312
313 /*
314 ===============
315 idMaterial::MatchToken
316
317 Sets defaultShader and returns false if the next token doesn't match
318 ===============
319 */
320 bool idMaterial::MatchToken( idLexer &src, const char *match ) {
321         if ( !src.ExpectTokenString( match ) ) {
322                 SetMaterialFlag( MF_DEFAULTED );
323                 return false;
324         }
325         return true;
326 }
327
328 /*
329 =================
330 idMaterial::ParseSort
331 =================
332 */
333 void idMaterial::ParseSort( idLexer &src ) {
334         idToken token;
335
336         if ( !src.ReadTokenOnLine( &token ) ) {
337                 src.Warning( "missing sort parameter" );
338                 SetMaterialFlag( MF_DEFAULTED );
339                 return;
340         }
341
342         if ( !token.Icmp( "subview" ) ) {
343                 sort = SS_SUBVIEW;
344         } else if ( !token.Icmp( "opaque" ) ) {
345                 sort = SS_OPAQUE;
346         }else if ( !token.Icmp( "decal" ) ) {
347                 sort = SS_DECAL;
348         } else if ( !token.Icmp( "far" ) ) {
349                 sort = SS_FAR;
350         } else if ( !token.Icmp( "medium" ) ) {
351                 sort = SS_MEDIUM;
352         } else if ( !token.Icmp( "close" ) ) {
353                 sort = SS_CLOSE;
354         } else if ( !token.Icmp( "almostNearest" ) ) {
355                 sort = SS_ALMOST_NEAREST;
356         } else if ( !token.Icmp( "nearest" ) ) {
357                 sort = SS_NEAREST;
358         } else if ( !token.Icmp( "postProcess" ) ) {
359                 sort = SS_POST_PROCESS;
360         } else if ( !token.Icmp( "portalSky" ) ) {
361                 sort = SS_PORTAL_SKY;
362         } else {
363                 sort = atof( token );
364         }
365 }
366
367 /*
368 =================
369 idMaterial::ParseDecalInfo
370 =================
371 */
372 void idMaterial::ParseDecalInfo( idLexer &src ) {
373         idToken token;
374
375         decalInfo.stayTime = src.ParseFloat() * 1000;
376         decalInfo.fadeTime = src.ParseFloat() * 1000;
377         float   start[4], end[4];
378         src.Parse1DMatrix( 4, start );
379         src.Parse1DMatrix( 4, end );
380         for ( int i = 0 ; i < 4 ; i++ ) {
381                 decalInfo.start[i] = start[i];
382                 decalInfo.end[i] = end[i];
383         }
384 }
385
386 /*
387 =============
388 idMaterial::GetExpressionConstant
389 =============
390 */
391 int idMaterial::GetExpressionConstant( float f ) {
392         int             i;
393
394         for ( i = EXP_REG_NUM_PREDEFINED ; i < numRegisters ; i++ ) {
395                 if ( !pd->registerIsTemporary[i] && pd->shaderRegisters[i] == f ) {
396                         return i;
397                 }
398         }
399         if ( numRegisters == MAX_EXPRESSION_REGISTERS ) {
400                 common->Warning( "GetExpressionConstant: material '%s' hit MAX_EXPRESSION_REGISTERS", GetName() );
401                 SetMaterialFlag( MF_DEFAULTED );
402                 return 0;
403         }
404         pd->registerIsTemporary[i] = false;
405         pd->shaderRegisters[i] = f;
406         numRegisters++;
407
408         return i;
409 }
410
411 /*
412 =============
413 idMaterial::GetExpressionTemporary
414 =============
415 */
416 int idMaterial::GetExpressionTemporary( void ) {
417         if ( numRegisters == MAX_EXPRESSION_REGISTERS ) {
418                 common->Warning( "GetExpressionTemporary: material '%s' hit MAX_EXPRESSION_REGISTERS", GetName() );
419                 SetMaterialFlag( MF_DEFAULTED );
420                 return 0;
421         }
422         pd->registerIsTemporary[numRegisters] = true;
423         numRegisters++;
424         return numRegisters - 1;
425 }
426
427 /*
428 =============
429 idMaterial::GetExpressionOp
430 =============
431 */
432 expOp_t *idMaterial::GetExpressionOp( void ) {
433         if ( numOps == MAX_EXPRESSION_OPS ) {
434                 common->Warning( "GetExpressionOp: material '%s' hit MAX_EXPRESSION_OPS", GetName() );
435                 SetMaterialFlag( MF_DEFAULTED );
436                 return &pd->shaderOps[0];
437         }
438
439         return &pd->shaderOps[numOps++];
440 }
441
442 /*
443 =================
444 idMaterial::EmitOp
445 =================
446 */
447 int idMaterial::EmitOp( int a, int b, expOpType_t opType ) {
448         expOp_t *op;
449
450         // optimize away identity operations
451         if ( opType == OP_TYPE_ADD ) {
452                 if ( !pd->registerIsTemporary[a] && pd->shaderRegisters[a] == 0 ) {
453                         return b;
454                 }
455                 if ( !pd->registerIsTemporary[b] && pd->shaderRegisters[b] == 0 ) {
456                         return a;
457                 }
458                 if ( !pd->registerIsTemporary[a] && !pd->registerIsTemporary[b] ) {
459                         return GetExpressionConstant( pd->shaderRegisters[a] + pd->shaderRegisters[b] );
460                 }
461         }
462         if ( opType == OP_TYPE_MULTIPLY ) {
463                 if ( !pd->registerIsTemporary[a] && pd->shaderRegisters[a] == 1 ) {
464                         return b;
465                 }
466                 if ( !pd->registerIsTemporary[a] && pd->shaderRegisters[a] == 0 ) {
467                         return a;
468                 }
469                 if ( !pd->registerIsTemporary[b] && pd->shaderRegisters[b] == 1 ) {
470                         return a;
471                 }
472                 if ( !pd->registerIsTemporary[b] && pd->shaderRegisters[b] == 0 ) {
473                         return b;
474                 }
475                 if ( !pd->registerIsTemporary[a] && !pd->registerIsTemporary[b] ) {
476                         return GetExpressionConstant( pd->shaderRegisters[a] * pd->shaderRegisters[b] );
477                 }
478         }
479
480         op = GetExpressionOp();
481         op->opType = opType;
482         op->a = a;
483         op->b = b;
484         op->c = GetExpressionTemporary();
485
486         return op->c;
487 }
488
489 /*
490 =================
491 idMaterial::ParseEmitOp
492 =================
493 */
494 int idMaterial::ParseEmitOp( idLexer &src, int a, expOpType_t opType, int priority ) {
495         int             b;
496
497         b = ParseExpressionPriority( src, priority );
498         return EmitOp( a, b, opType );
499 }
500
501 /*
502 =================
503 idMaterial::ParseTerm
504
505 Returns a register index
506 =================
507 */
508 int idMaterial::ParseTerm( idLexer &src ) {
509         idToken token;
510         int             a, b;
511
512         src.ReadToken( &token );
513
514         if ( token == "(" ) {
515                 a = ParseExpression( src );
516                 MatchToken( src, ")" );
517                 return a;
518         }
519
520         if ( !token.Icmp( "time" ) ) {
521                 pd->registersAreConstant = false;
522                 return EXP_REG_TIME;
523         }
524         if ( !token.Icmp( "parm0" ) ) {
525                 pd->registersAreConstant = false;
526                 return EXP_REG_PARM0;
527         }
528         if ( !token.Icmp( "parm1" ) ) {
529                 pd->registersAreConstant = false;
530                 return EXP_REG_PARM1;
531         }
532         if ( !token.Icmp( "parm2" ) ) {
533                 pd->registersAreConstant = false;
534                 return EXP_REG_PARM2;
535         }
536         if ( !token.Icmp( "parm3" ) ) {
537                 pd->registersAreConstant = false;
538                 return EXP_REG_PARM3;
539         }
540         if ( !token.Icmp( "parm4" ) ) {
541                 pd->registersAreConstant = false;
542                 return EXP_REG_PARM4;
543         }
544         if ( !token.Icmp( "parm5" ) ) {
545                 pd->registersAreConstant = false;
546                 return EXP_REG_PARM5;
547         }
548         if ( !token.Icmp( "parm6" ) ) {
549                 pd->registersAreConstant = false;
550                 return EXP_REG_PARM6;
551         }
552         if ( !token.Icmp( "parm7" ) ) {
553                 pd->registersAreConstant = false;
554                 return EXP_REG_PARM7;
555         }
556         if ( !token.Icmp( "parm8" ) ) {
557                 pd->registersAreConstant = false;
558                 return EXP_REG_PARM8;
559         }
560         if ( !token.Icmp( "parm9" ) ) {
561                 pd->registersAreConstant = false;
562                 return EXP_REG_PARM9;
563         }
564         if ( !token.Icmp( "parm10" ) ) {
565                 pd->registersAreConstant = false;
566                 return EXP_REG_PARM10;
567         }
568         if ( !token.Icmp( "parm11" ) ) {
569                 pd->registersAreConstant = false;
570                 return EXP_REG_PARM11;
571         }
572         if ( !token.Icmp( "global0" ) ) {
573                 pd->registersAreConstant = false;
574                 return EXP_REG_GLOBAL0;
575         }
576         if ( !token.Icmp( "global1" ) ) {
577                 pd->registersAreConstant = false;
578                 return EXP_REG_GLOBAL1;
579         }
580         if ( !token.Icmp( "global2" ) ) {
581                 pd->registersAreConstant = false;
582                 return EXP_REG_GLOBAL2;
583         }
584         if ( !token.Icmp( "global3" ) ) {
585                 pd->registersAreConstant = false;
586                 return EXP_REG_GLOBAL3;
587         }
588         if ( !token.Icmp( "global4" ) ) {
589                 pd->registersAreConstant = false;
590                 return EXP_REG_GLOBAL4;
591         }
592         if ( !token.Icmp( "global5" ) ) {
593                 pd->registersAreConstant = false;
594                 return EXP_REG_GLOBAL5;
595         }
596         if ( !token.Icmp( "global6" ) ) {
597                 pd->registersAreConstant = false;
598                 return EXP_REG_GLOBAL6;
599         }
600         if ( !token.Icmp( "global7" ) ) {
601                 pd->registersAreConstant = false;
602                 return EXP_REG_GLOBAL7;
603         }
604         if ( !token.Icmp( "fragmentPrograms" ) ) {
605                 return GetExpressionConstant( (float) glConfig.ARBFragmentProgramAvailable );
606         }
607
608         if ( !token.Icmp( "sound" ) ) {
609                 pd->registersAreConstant = false;
610                 return EmitOp( 0, 0, OP_TYPE_SOUND );
611         }
612
613         // parse negative numbers
614         if ( token == "-" ) {
615                 src.ReadToken( &token );
616                 if ( token.type == TT_NUMBER || token == "." ) {
617                         return GetExpressionConstant( -(float) token.GetFloatValue() );
618                 }
619                 src.Warning( "Bad negative number '%s'", token.c_str() );
620                 SetMaterialFlag( MF_DEFAULTED );
621                 return 0;
622         }
623
624         if ( token.type == TT_NUMBER || token == "." || token == "-" ) {
625                 return GetExpressionConstant( (float) token.GetFloatValue() );
626         }
627
628         // see if it is a table name
629         const idDeclTable *table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, token.c_str(), false ) );
630         if ( !table ) {
631                 src.Warning( "Bad term '%s'", token.c_str() );
632                 SetMaterialFlag( MF_DEFAULTED );
633                 return 0;
634         }
635
636         // parse a table expression
637         MatchToken( src, "[" );
638
639         b = ParseExpression( src );
640
641         MatchToken( src, "]" );
642
643         return EmitOp( table->Index(), b, OP_TYPE_TABLE );
644 }
645
646 /*
647 =================
648 idMaterial::ParseExpressionPriority
649
650 Returns a register index
651 =================
652 */
653 #define TOP_PRIORITY 4
654 int idMaterial::ParseExpressionPriority( idLexer &src, int priority ) {
655         idToken token;
656         int             a;
657
658         if ( priority == 0 ) {
659                 return ParseTerm( src );
660         }
661
662         a = ParseExpressionPriority( src, priority - 1 );
663
664         if ( TestMaterialFlag( MF_DEFAULTED ) ) {       // we have a parse error
665                 return 0;
666         }
667
668         if ( !src.ReadToken( &token ) ) {
669                 // we won't get EOF in a real file, but we can
670                 // when parsing from generated strings
671                 return a;
672         }
673
674         if ( priority == 1 && token == "*" ) {
675                 return ParseEmitOp( src, a, OP_TYPE_MULTIPLY, priority );
676         }
677         if ( priority == 1 && token == "/" ) {
678                 return ParseEmitOp( src, a, OP_TYPE_DIVIDE, priority );
679         }
680         if ( priority == 1 && token == "%" ) {  // implied truncate both to integer
681                 return ParseEmitOp( src, a, OP_TYPE_MOD, priority );
682         }
683         if ( priority == 2 && token == "+" ) {
684                 return ParseEmitOp( src, a, OP_TYPE_ADD, priority );
685         }
686         if ( priority == 2 && token == "-" ) {
687                 return ParseEmitOp( src, a, OP_TYPE_SUBTRACT, priority );
688         }
689         if ( priority == 3 && token == ">" ) {
690                 return ParseEmitOp( src, a, OP_TYPE_GT, priority );
691         }
692         if ( priority == 3 && token == ">=" ) {
693                 return ParseEmitOp( src, a, OP_TYPE_GE, priority );
694         }
695         if ( priority == 3 && token == "<" ) {
696                 return ParseEmitOp( src, a, OP_TYPE_LT, priority );
697         }
698         if ( priority == 3 && token == "<=" ) {
699                 return ParseEmitOp( src, a, OP_TYPE_LE, priority );
700         }
701         if ( priority == 3 && token == "==" ) {
702                 return ParseEmitOp( src, a, OP_TYPE_EQ, priority );
703         }
704         if ( priority == 3 && token == "!=" ) {
705                 return ParseEmitOp( src, a, OP_TYPE_NE, priority );
706         }
707         if ( priority == 4 && token == "&&" ) {
708                 return ParseEmitOp( src, a, OP_TYPE_AND, priority );
709         }
710         if ( priority == 4 && token == "||" ) {
711                 return ParseEmitOp( src, a, OP_TYPE_OR, priority );
712         }
713
714         // assume that anything else terminates the expression
715         // not too robust error checking...
716
717         src.UnreadToken( &token );
718
719         return a;
720 }
721
722 /*
723 =================
724 idMaterial::ParseExpression
725
726 Returns a register index
727 =================
728 */
729 int idMaterial::ParseExpression( idLexer &src ) {
730         return ParseExpressionPriority( src, TOP_PRIORITY );
731 }
732
733
734 /*
735 ===============
736 idMaterial::ClearStage
737 ===============
738 */
739 void idMaterial::ClearStage( shaderStage_t *ss ) {
740         ss->drawStateBits = 0;
741         ss->conditionRegister = GetExpressionConstant( 1 );
742         ss->color.registers[0] =
743         ss->color.registers[1] =
744         ss->color.registers[2] =
745         ss->color.registers[3] = GetExpressionConstant( 1 );
746 }
747
748 /*
749 ===============
750 idMaterial::NameToSrcBlendMode
751 ===============
752 */
753 int idMaterial::NameToSrcBlendMode( const idStr &name ) {
754         if ( !name.Icmp( "GL_ONE" ) ) {
755                 return GLS_SRCBLEND_ONE;
756         } else if ( !name.Icmp( "GL_ZERO" ) ) {
757                 return GLS_SRCBLEND_ZERO;
758         } else if ( !name.Icmp( "GL_DST_COLOR" ) ) {
759                 return GLS_SRCBLEND_DST_COLOR;
760         } else if ( !name.Icmp( "GL_ONE_MINUS_DST_COLOR" ) ) {
761                 return GLS_SRCBLEND_ONE_MINUS_DST_COLOR;
762         } else if ( !name.Icmp( "GL_SRC_ALPHA" ) ) {
763                 return GLS_SRCBLEND_SRC_ALPHA;
764         } else if ( !name.Icmp( "GL_ONE_MINUS_SRC_ALPHA" ) ) {
765                 return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA;
766         } else if ( !name.Icmp( "GL_DST_ALPHA" ) ) {
767                 return GLS_SRCBLEND_DST_ALPHA;
768         } else if ( !name.Icmp( "GL_ONE_MINUS_DST_ALPHA" ) ) {
769                 return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA;
770         } else if ( !name.Icmp( "GL_SRC_ALPHA_SATURATE" ) ) {
771                 return GLS_SRCBLEND_ALPHA_SATURATE;
772         }
773
774         common->Warning( "unknown blend mode '%s' in material '%s'", name.c_str(), GetName() );
775         SetMaterialFlag( MF_DEFAULTED );
776
777         return GLS_SRCBLEND_ONE;
778 }
779
780 /*
781 ===============
782 idMaterial::NameToDstBlendMode
783 ===============
784 */
785 int idMaterial::NameToDstBlendMode( const idStr &name ) {
786         if ( !name.Icmp( "GL_ONE" ) ) {
787                 return GLS_DSTBLEND_ONE;
788         } else if ( !name.Icmp( "GL_ZERO" ) ) {
789                 return GLS_DSTBLEND_ZERO;
790         } else if ( !name.Icmp( "GL_SRC_ALPHA" ) ) {
791                 return GLS_DSTBLEND_SRC_ALPHA;
792         } else if ( !name.Icmp( "GL_ONE_MINUS_SRC_ALPHA" ) ) {
793                 return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
794         } else if ( !name.Icmp( "GL_DST_ALPHA" ) ) {
795                 return GLS_DSTBLEND_DST_ALPHA;
796         } else if ( !name.Icmp( "GL_ONE_MINUS_DST_ALPHA" ) ) {
797                 return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA;
798         } else if ( !name.Icmp( "GL_SRC_COLOR" ) ) {
799                 return GLS_DSTBLEND_SRC_COLOR;
800         } else if ( !name.Icmp( "GL_ONE_MINUS_SRC_COLOR" ) ) {
801                 return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR;
802         }
803
804         common->Warning( "unknown blend mode '%s' in material '%s'", name.c_str(), GetName() );
805         SetMaterialFlag( MF_DEFAULTED );
806
807         return GLS_DSTBLEND_ONE;
808 }
809
810 /*
811 ================
812 idMaterial::ParseBlend
813 ================
814 */
815 void idMaterial::ParseBlend( idLexer &src, shaderStage_t *stage ) {
816         idToken token;
817         int             srcBlend, dstBlend;
818
819         if ( !src.ReadToken( &token ) ) {
820                 return;
821         }
822
823         // blending combinations
824         if ( !token.Icmp( "blend" ) ) {
825                 stage->drawStateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
826                 return;
827         }
828         if ( !token.Icmp( "add" ) ) {
829                 stage->drawStateBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE;
830                 return;
831         }
832         if ( !token.Icmp( "filter" ) || !token.Icmp( "modulate" ) ) {
833                 stage->drawStateBits = GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO;
834                 return;
835         }
836         if (  !token.Icmp( "none" ) ) {
837                 // none is used when defining an alpha mask that doesn't draw
838                 stage->drawStateBits = GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE;
839                 return;
840         }
841         if ( !token.Icmp( "bumpmap" ) ) {
842                 stage->lighting = SL_BUMP;
843                 return;
844         }
845         if ( !token.Icmp( "diffusemap" ) ) {
846                 stage->lighting = SL_DIFFUSE;
847                 return;
848         }
849         if ( !token.Icmp( "specularmap" ) ) {
850                 stage->lighting = SL_SPECULAR;
851                 return;
852         }
853
854         srcBlend = NameToSrcBlendMode( token );
855
856         MatchToken( src, "," );
857         if ( !src.ReadToken( &token ) ) {
858                 return;
859         }
860         dstBlend = NameToDstBlendMode( token );
861
862         stage->drawStateBits = srcBlend | dstBlend;
863 }
864
865 /*
866 ================
867 idMaterial::ParseVertexParm
868
869 If there is a single value, it will be repeated across all elements
870 If there are two values, 3 = 0.0, 4 = 1.0
871 if there are three values, 4 = 1.0
872 ================
873 */
874 void idMaterial::ParseVertexParm( idLexer &src, newShaderStage_t *newStage ) {
875         idToken                         token;
876
877         src.ReadTokenOnLine( &token );
878         int     parm = token.GetIntValue();
879         if ( !token.IsNumeric() || parm < 0 || parm >= MAX_VERTEX_PARMS ) {
880                 common->Warning( "bad vertexParm number\n" );
881                 SetMaterialFlag( MF_DEFAULTED );
882                 return;
883         }
884         if ( parm >= newStage->numVertexParms ) {
885                 newStage->numVertexParms = parm+1;
886         }
887
888         newStage->vertexParms[parm][0] = ParseExpression( src );
889
890         src.ReadTokenOnLine( &token );
891         if ( !token[0] || token.Icmp( "," ) ) {
892                 newStage->vertexParms[parm][1] =
893                 newStage->vertexParms[parm][2] =
894                 newStage->vertexParms[parm][3] = newStage->vertexParms[parm][0];
895                 return;
896         }
897
898         newStage->vertexParms[parm][1] = ParseExpression( src );
899
900         src.ReadTokenOnLine( &token );
901         if ( !token[0] || token.Icmp( "," ) ) {
902                 newStage->vertexParms[parm][2] = GetExpressionConstant( 0 );
903                 newStage->vertexParms[parm][3] = GetExpressionConstant( 1 );
904                 return;
905         }
906
907         newStage->vertexParms[parm][2] = ParseExpression( src );
908
909         src.ReadTokenOnLine( &token );
910         if ( !token[0] || token.Icmp( "," ) ) {
911                 newStage->vertexParms[parm][3] = GetExpressionConstant( 1 );
912                 return;
913         }
914
915         newStage->vertexParms[parm][3] = ParseExpression( src );
916 }
917
918
919 /*
920 ================
921 idMaterial::ParseFragmentMap
922 ================
923 */
924 void idMaterial::ParseFragmentMap( idLexer &src, newShaderStage_t *newStage ) {
925         const char                      *str;
926         textureFilter_t         tf;
927         textureRepeat_t         trp;
928         textureDepth_t          td;
929         cubeFiles_t                     cubeMap;
930         bool                            allowPicmip;
931         idToken                         token;
932
933         tf = TF_DEFAULT;
934         trp = TR_REPEAT;
935         td = TD_DEFAULT;
936         allowPicmip = true;
937         cubeMap = CF_2D;
938
939         src.ReadTokenOnLine( &token );
940         int     unit = token.GetIntValue();
941         if ( !token.IsNumeric() || unit < 0 || unit >= MAX_FRAGMENT_IMAGES ) {
942                 common->Warning( "bad fragmentMap number\n" );
943                 SetMaterialFlag( MF_DEFAULTED );
944                 return;
945         }
946
947         // unit 1 is the normal map.. make sure it gets flagged as the proper depth
948         if ( unit == 1 ) {
949                 td = TD_BUMP;
950         }
951
952         if ( unit >= newStage->numFragmentProgramImages ) {
953                 newStage->numFragmentProgramImages = unit+1;
954         }
955
956         while( 1 ) {
957                 src.ReadTokenOnLine( &token );
958
959                 if ( !token.Icmp( "cubeMap" ) ) {
960                         cubeMap = CF_NATIVE;
961                         continue;
962                 }
963                 if ( !token.Icmp( "cameraCubeMap" ) ) {
964                         cubeMap = CF_CAMERA;
965                         continue;
966                 }
967                 if ( !token.Icmp( "nearest" ) ) {
968                         tf = TF_NEAREST;
969                         continue;
970                 }
971                 if ( !token.Icmp( "linear" ) ) {
972                         tf = TF_LINEAR;
973                         continue;
974                 }
975                 if ( !token.Icmp( "clamp" ) ) {
976                         trp = TR_CLAMP;
977                         continue;
978                 }
979                 if ( !token.Icmp( "noclamp" ) ) {
980                         trp = TR_REPEAT;
981                         continue;
982                 }
983                 if ( !token.Icmp( "zeroclamp" ) ) {
984                         trp = TR_CLAMP_TO_ZERO;
985                         continue;
986                 }
987                 if ( !token.Icmp( "alphazeroclamp" ) ) {
988                         trp = TR_CLAMP_TO_ZERO_ALPHA;
989                         continue;
990                 }
991                 if ( !token.Icmp( "forceHighQuality" ) ) {
992                         td = TD_HIGH_QUALITY;
993                         continue;
994                 }
995
996                 if ( !token.Icmp( "uncompressed" ) || !token.Icmp( "highquality" ) ) {
997                         if ( !globalImages->image_ignoreHighQuality.GetInteger() ) {
998                                 td = TD_HIGH_QUALITY;
999                         }
1000                         continue;
1001                 }
1002                 if ( !token.Icmp( "nopicmip" ) ) {
1003                         allowPicmip = false;
1004                         continue;
1005                 }
1006
1007                 // assume anything else is the image name
1008                 src.UnreadToken( &token );
1009                 break;
1010         }
1011         str = R_ParsePastImageProgram( src );
1012
1013         newStage->fragmentProgramImages[unit] = 
1014                 globalImages->ImageFromFile( str, tf, allowPicmip, trp, td, cubeMap );
1015         if ( !newStage->fragmentProgramImages[unit] ) {
1016                 newStage->fragmentProgramImages[unit] = globalImages->defaultImage;
1017         }
1018 }
1019
1020 /*
1021 ===============
1022 idMaterial::MultiplyTextureMatrix
1023 ===============
1024 */
1025 void idMaterial::MultiplyTextureMatrix( textureStage_t *ts, int registers[2][3] ) {
1026         int             old[2][3];
1027
1028         if ( !ts->hasMatrix ) {
1029                 ts->hasMatrix = true;
1030                 memcpy( ts->matrix, registers, sizeof( ts->matrix ) );
1031                 return;
1032         }
1033
1034         memcpy( old, ts->matrix, sizeof( old ) );
1035
1036         // multiply the two maticies
1037         ts->matrix[0][0] = EmitOp(
1038                                                         EmitOp( old[0][0], registers[0][0], OP_TYPE_MULTIPLY ),
1039                                                         EmitOp( old[0][1], registers[1][0], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1040         ts->matrix[0][1] = EmitOp(
1041                                                         EmitOp( old[0][0], registers[0][1], OP_TYPE_MULTIPLY ),
1042                                                         EmitOp( old[0][1], registers[1][1], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1043         ts->matrix[0][2] = EmitOp( 
1044                                                         EmitOp(
1045                                                                 EmitOp( old[0][0], registers[0][2], OP_TYPE_MULTIPLY ),
1046                                                                 EmitOp( old[0][1], registers[1][2], OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1047                                                         old[0][2], OP_TYPE_ADD );
1048
1049         ts->matrix[1][0] = EmitOp(
1050                                                         EmitOp( old[1][0], registers[0][0], OP_TYPE_MULTIPLY ),
1051                                                         EmitOp( old[1][1], registers[1][0], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1052         ts->matrix[1][1] = EmitOp(
1053                                                         EmitOp( old[1][0], registers[0][1], OP_TYPE_MULTIPLY ),
1054                                                         EmitOp( old[1][1], registers[1][1], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1055         ts->matrix[1][2] = EmitOp( 
1056                                                         EmitOp(
1057                                                                 EmitOp( old[1][0], registers[0][2], OP_TYPE_MULTIPLY ),
1058                                                                 EmitOp( old[1][1], registers[1][2], OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1059                                                         old[1][2], OP_TYPE_ADD );
1060
1061 }
1062
1063 /*
1064 =================
1065 idMaterial::ParseStage
1066
1067 An open brace has been parsed
1068
1069
1070 {
1071         if <expression>
1072         map <imageprogram>
1073         "nearest" "linear" "clamp" "zeroclamp" "uncompressed" "highquality" "nopicmip"
1074         scroll, scale, rotate
1075 }
1076
1077 =================
1078 */
1079 void idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
1080         idToken                         token;
1081         const char                      *str;
1082         shaderStage_t           *ss;
1083         textureStage_t          *ts;
1084         textureFilter_t         tf;
1085         textureRepeat_t         trp;
1086         textureDepth_t          td;
1087         cubeFiles_t                     cubeMap;
1088         bool                            allowPicmip;
1089         char                            imageName[MAX_IMAGE_NAME];
1090         int                                     a, b;
1091         int                                     matrix[2][3];
1092         newShaderStage_t        newStage;
1093
1094         if ( numStages >= MAX_SHADER_STAGES ) {
1095                 SetMaterialFlag( MF_DEFAULTED );
1096                 common->Warning( "material '%s' exceeded %i stages", GetName(), MAX_SHADER_STAGES );
1097         }
1098
1099         tf = TF_DEFAULT;
1100         trp = trpDefault;
1101         td = TD_DEFAULT;
1102         allowPicmip = true;
1103         cubeMap = CF_2D;
1104
1105         imageName[0] = 0;
1106
1107         memset( &newStage, 0, sizeof( newStage ) );
1108
1109         ss = &pd->parseStages[numStages];
1110         ts = &ss->texture;
1111
1112         ClearStage( ss );
1113
1114         while ( 1 ) {
1115                 if ( TestMaterialFlag( MF_DEFAULTED ) ) {       // we have a parse error
1116                         return;
1117                 }
1118                 if ( !src.ExpectAnyToken( &token ) ) {
1119                         SetMaterialFlag( MF_DEFAULTED );
1120                         return;
1121                 }
1122
1123                 // the close brace for the entire material ends the draw block
1124                 if ( token == "}" ) {
1125                         break;
1126                 }
1127
1128                 //BSM Nerve: Added for stage naming in the material editor
1129                 if( !token.Icmp( "name") ) {
1130                         src.SkipRestOfLine();
1131                         continue;
1132                 }
1133
1134                 // image options
1135                 if ( !token.Icmp( "blend" ) ) {
1136                         ParseBlend( src, ss );
1137                         continue;
1138                 }
1139
1140                 if (  !token.Icmp( "map" ) ) {
1141                         str = R_ParsePastImageProgram( src );
1142                         idStr::Copynz( imageName, str, sizeof( imageName ) );
1143                         continue;
1144                 }
1145
1146                 if (  !token.Icmp( "remoteRenderMap" ) ) {
1147                         ts->dynamic = DI_REMOTE_RENDER;
1148                         ts->width = src.ParseInt();
1149                         ts->height = src.ParseInt();
1150                         continue;
1151                 }
1152
1153                 if (  !token.Icmp( "mirrorRenderMap" ) ) {
1154                         ts->dynamic = DI_MIRROR_RENDER;
1155                         ts->width = src.ParseInt();
1156                         ts->height = src.ParseInt();
1157                         ts->texgen = TG_SCREEN;
1158                         continue;
1159                 }
1160
1161                 if (  !token.Icmp( "xrayRenderMap" ) ) {
1162                         ts->dynamic = DI_XRAY_RENDER;
1163                         ts->width = src.ParseInt();
1164                         ts->height = src.ParseInt();
1165                         ts->texgen = TG_SCREEN;
1166                         continue;
1167                 }
1168                 if (  !token.Icmp( "screen" ) ) {
1169                         ts->texgen = TG_SCREEN;
1170                         continue;
1171                 }
1172                 if (  !token.Icmp( "screen2" ) ) {
1173                         ts->texgen = TG_SCREEN2;
1174                         continue;
1175                 }
1176                 if (  !token.Icmp( "glassWarp" ) ) {
1177                         ts->texgen = TG_GLASSWARP;
1178                         continue;
1179                 }
1180
1181                 if ( !token.Icmp( "videomap" ) ) {
1182                         // note that videomaps will always be in clamp mode, so texture
1183                         // coordinates had better be in the 0 to 1 range
1184                         if ( !src.ReadToken( &token ) ) {
1185                                 common->Warning( "missing parameter for 'videoMap' keyword in material '%s'", GetName() );
1186                                 continue;
1187                         }
1188                         bool loop = false;
1189                         if ( !token.Icmp( "loop" ) ) {
1190                                 loop = true;
1191                                 if ( !src.ReadToken( &token ) ) {
1192                                         common->Warning( "missing parameter for 'videoMap' keyword in material '%s'", GetName() );
1193                                         continue;
1194                                 }
1195                         }
1196                         ts->cinematic = idCinematic::Alloc();
1197                         ts->cinematic->InitFromFile( token.c_str(), loop );
1198                         continue;
1199                 }
1200
1201                 if ( !token.Icmp( "soundmap" ) ) {
1202                         if ( !src.ReadToken( &token ) ) {
1203                                 common->Warning( "missing parameter for 'soundmap' keyword in material '%s'", GetName() );
1204                                 continue;
1205                         }
1206                         ts->cinematic = new idSndWindow();
1207                         ts->cinematic->InitFromFile( token.c_str(), true );
1208                         continue;
1209                 }
1210
1211                 if ( !token.Icmp( "cubeMap" ) ) {
1212                         str = R_ParsePastImageProgram( src );
1213                         idStr::Copynz( imageName, str, sizeof( imageName ) );
1214                         cubeMap = CF_NATIVE;
1215                         continue;
1216                 }
1217
1218                 if ( !token.Icmp( "cameraCubeMap" ) ) {
1219                         str = R_ParsePastImageProgram( src );
1220                         idStr::Copynz( imageName, str, sizeof( imageName ) );
1221                         cubeMap = CF_CAMERA;
1222                         continue;
1223                 }
1224
1225                 if ( !token.Icmp( "ignoreAlphaTest" ) ) {
1226                         ss->ignoreAlphaTest = true;
1227                         continue;
1228                 }
1229                 if ( !token.Icmp( "nearest" ) ) {
1230                         tf = TF_NEAREST;
1231                         continue;
1232                 }
1233                 if ( !token.Icmp( "linear" ) ) {
1234                         tf = TF_LINEAR;
1235                         continue;
1236                 }
1237                 if ( !token.Icmp( "clamp" ) ) {
1238                         trp = TR_CLAMP;
1239                         continue;
1240                 }
1241                 if ( !token.Icmp( "noclamp" ) ) {
1242                         trp = TR_REPEAT;
1243                         continue;
1244                 }
1245                 if ( !token.Icmp( "zeroclamp" ) ) {
1246                         trp = TR_CLAMP_TO_ZERO;
1247                         continue;
1248                 }
1249                 if ( !token.Icmp( "alphazeroclamp" ) ) {
1250                         trp = TR_CLAMP_TO_ZERO_ALPHA;
1251                         continue;
1252                 }
1253                 if ( !token.Icmp( "uncompressed" ) || !token.Icmp( "highquality" ) ) {
1254                         if ( !globalImages->image_ignoreHighQuality.GetInteger() ) {
1255                                 td = TD_HIGH_QUALITY;
1256                         }
1257                         continue;
1258                 }
1259                 if ( !token.Icmp( "forceHighQuality" ) ) {
1260                         td = TD_HIGH_QUALITY;
1261                         continue;
1262                 }
1263                 if ( !token.Icmp( "nopicmip" ) ) {
1264                         allowPicmip = false;
1265                         continue;
1266                 }
1267                 if ( !token.Icmp( "vertexColor" ) ) {
1268                         ss->vertexColor = SVC_MODULATE;
1269                         continue;
1270                 }
1271                 if ( !token.Icmp( "inverseVertexColor" ) ) {
1272                         ss->vertexColor = SVC_INVERSE_MODULATE;
1273                         continue;
1274                 }
1275
1276                 // privatePolygonOffset
1277                 else if ( !token.Icmp( "privatePolygonOffset" ) ) {
1278                         if ( !src.ReadTokenOnLine( &token ) ) {
1279                                 ss->privatePolygonOffset = 1;
1280                                 continue;
1281                         }
1282                         // explict larger (or negative) offset
1283                         src.UnreadToken( &token );
1284                         ss->privatePolygonOffset = src.ParseFloat();
1285                         continue;
1286                 }
1287
1288                 // texture coordinate generation
1289                 if ( !token.Icmp( "texGen" ) ) {
1290                         src.ExpectAnyToken( &token );
1291                         if ( !token.Icmp( "normal" ) ) {
1292                                 ts->texgen = TG_DIFFUSE_CUBE;
1293                         } else if ( !token.Icmp( "reflect" ) ) {
1294                                 ts->texgen = TG_REFLECT_CUBE;
1295                         } else if ( !token.Icmp( "skybox" ) ) {
1296                                 ts->texgen = TG_SKYBOX_CUBE;
1297                         } else if ( !token.Icmp( "wobbleSky" ) ) {
1298                                 ts->texgen = TG_WOBBLESKY_CUBE;
1299                                 texGenRegisters[0] = ParseExpression( src );
1300                                 texGenRegisters[1] = ParseExpression( src );
1301                                 texGenRegisters[2] = ParseExpression( src );
1302                         } else {
1303                                 common->Warning( "bad texGen '%s' in material %s", token.c_str(), GetName() );
1304                                 SetMaterialFlag( MF_DEFAULTED );
1305                         }
1306                         continue;
1307                 }
1308                 if ( !token.Icmp( "scroll" ) || !token.Icmp( "translate" ) ) {
1309                         a = ParseExpression( src );
1310                         MatchToken( src, "," );
1311                         b = ParseExpression( src );
1312                         matrix[0][0] = GetExpressionConstant( 1 );
1313                         matrix[0][1] = GetExpressionConstant( 0 );
1314                         matrix[0][2] = a;
1315                         matrix[1][0] = GetExpressionConstant( 0 );
1316                         matrix[1][1] = GetExpressionConstant( 1 );
1317                         matrix[1][2] = b;
1318
1319                         MultiplyTextureMatrix( ts, matrix );
1320                         continue;
1321                 }
1322                 if ( !token.Icmp( "scale" ) ) {
1323                         a = ParseExpression( src );
1324                         MatchToken( src, "," );
1325                         b = ParseExpression( src );
1326                         // this just scales without a centering
1327                         matrix[0][0] = a;
1328                         matrix[0][1] = GetExpressionConstant( 0 );
1329                         matrix[0][2] = GetExpressionConstant( 0 );
1330                         matrix[1][0] = GetExpressionConstant( 0 );
1331                         matrix[1][1] = b;
1332                         matrix[1][2] = GetExpressionConstant( 0 );
1333
1334                         MultiplyTextureMatrix( ts, matrix );
1335                         continue;
1336                 }
1337                 if ( !token.Icmp( "centerScale" ) ) {
1338                         a = ParseExpression( src );
1339                         MatchToken( src, "," );
1340                         b = ParseExpression( src );
1341                         // this subtracts 0.5, then scales, then adds 0.5
1342                         matrix[0][0] = a;
1343                         matrix[0][1] = GetExpressionConstant( 0 );
1344                         matrix[0][2] = EmitOp( GetExpressionConstant( 0.5 ), EmitOp( GetExpressionConstant( 0.5 ), a, OP_TYPE_MULTIPLY ), OP_TYPE_SUBTRACT );
1345                         matrix[1][0] = GetExpressionConstant( 0 );
1346                         matrix[1][1] = b;
1347                         matrix[1][2] = EmitOp( GetExpressionConstant( 0.5 ), EmitOp( GetExpressionConstant( 0.5 ), b, OP_TYPE_MULTIPLY ), OP_TYPE_SUBTRACT );
1348
1349                         MultiplyTextureMatrix( ts, matrix );
1350                         continue;
1351                 }
1352                 if ( !token.Icmp( "shear" ) ) {
1353                         a = ParseExpression( src );
1354                         MatchToken( src, "," );
1355                         b = ParseExpression( src );
1356                         // this subtracts 0.5, then shears, then adds 0.5
1357                         matrix[0][0] = GetExpressionConstant( 1 );
1358                         matrix[0][1] = a;
1359                         matrix[0][2] = EmitOp( GetExpressionConstant( -0.5 ), a, OP_TYPE_MULTIPLY );
1360                         matrix[1][0] = b;
1361                         matrix[1][1] = GetExpressionConstant( 1 );
1362                         matrix[1][2] = EmitOp( GetExpressionConstant( -0.5 ), b, OP_TYPE_MULTIPLY );
1363
1364                         MultiplyTextureMatrix( ts, matrix );
1365                         continue;
1366                 }
1367                 if ( !token.Icmp( "rotate" ) ) {
1368                         const idDeclTable *table;
1369                         int             sinReg, cosReg;
1370
1371                         // in cycles
1372                         a = ParseExpression( src );
1373
1374                         table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, "sinTable", false ) );
1375                         if ( !table ) {
1376                                 common->Warning( "no sinTable for rotate defined" );
1377                                 SetMaterialFlag( MF_DEFAULTED );
1378                                 return;
1379                         }
1380                         sinReg = EmitOp( table->Index(), a, OP_TYPE_TABLE );
1381
1382                         table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, "cosTable", false ) );
1383                         if ( !table ) {
1384                                 common->Warning( "no cosTable for rotate defined" );
1385                                 SetMaterialFlag( MF_DEFAULTED );
1386                                 return;
1387                         }
1388                         cosReg = EmitOp( table->Index(), a, OP_TYPE_TABLE );
1389
1390                         // this subtracts 0.5, then rotates, then adds 0.5
1391                         matrix[0][0] = cosReg;
1392                         matrix[0][1] = EmitOp( GetExpressionConstant( 0 ), sinReg, OP_TYPE_SUBTRACT );
1393                         matrix[0][2] = EmitOp( EmitOp( EmitOp( GetExpressionConstant( -0.5 ), cosReg, OP_TYPE_MULTIPLY ), 
1394                                                                                 EmitOp( GetExpressionConstant( 0.5 ), sinReg, OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1395                                                                                 GetExpressionConstant( 0.5 ), OP_TYPE_ADD );
1396
1397                         matrix[1][0] = sinReg;
1398                         matrix[1][1] = cosReg;
1399                         matrix[1][2] = EmitOp( EmitOp( EmitOp( GetExpressionConstant( -0.5 ), sinReg, OP_TYPE_MULTIPLY ), 
1400                                                                                 EmitOp( GetExpressionConstant( -0.5 ), cosReg, OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1401                                                                                 GetExpressionConstant( 0.5 ), OP_TYPE_ADD );
1402
1403                         MultiplyTextureMatrix( ts, matrix );
1404                         continue;
1405                 }
1406
1407                 // color mask options
1408                 if ( !token.Icmp( "maskRed" ) ) {
1409                         ss->drawStateBits |= GLS_REDMASK;
1410                         continue;
1411                 }               
1412                 if ( !token.Icmp( "maskGreen" ) ) {
1413                         ss->drawStateBits |= GLS_GREENMASK;
1414                         continue;
1415                 }               
1416                 if ( !token.Icmp( "maskBlue" ) ) {
1417                         ss->drawStateBits |= GLS_BLUEMASK;
1418                         continue;
1419                 }               
1420                 if ( !token.Icmp( "maskAlpha" ) ) {
1421                         ss->drawStateBits |= GLS_ALPHAMASK;
1422                         continue;
1423                 }               
1424                 if ( !token.Icmp( "maskColor" ) ) {
1425                         ss->drawStateBits |= GLS_COLORMASK;
1426                         continue;
1427                 }               
1428                 if ( !token.Icmp( "maskDepth" ) ) {
1429                         ss->drawStateBits |= GLS_DEPTHMASK;
1430                         continue;
1431                 }               
1432                 if ( !token.Icmp( "alphaTest" ) ) {
1433                         ss->hasAlphaTest = true;
1434                         ss->alphaTestRegister = ParseExpression( src );
1435                         coverage = MC_PERFORATED;
1436                         continue;
1437                 }               
1438
1439                 // shorthand for 2D modulated
1440                 if ( !token.Icmp( "colored" ) ) {
1441                         ss->color.registers[0] = EXP_REG_PARM0;
1442                         ss->color.registers[1] = EXP_REG_PARM1;
1443                         ss->color.registers[2] = EXP_REG_PARM2;
1444                         ss->color.registers[3] = EXP_REG_PARM3;
1445                         pd->registersAreConstant = false;
1446                         continue;
1447                 }
1448
1449                 if ( !token.Icmp( "color" ) ) {
1450                         ss->color.registers[0] = ParseExpression( src );
1451                         MatchToken( src, "," );
1452                         ss->color.registers[1] = ParseExpression( src );
1453                         MatchToken( src, "," );
1454                         ss->color.registers[2] = ParseExpression( src );
1455                         MatchToken( src, "," );
1456                         ss->color.registers[3] = ParseExpression( src );
1457                         continue;
1458                 }
1459                 if ( !token.Icmp( "red" ) ) {
1460                         ss->color.registers[0] = ParseExpression( src );
1461                         continue;
1462                 }
1463                 if ( !token.Icmp( "green" ) ) {
1464                         ss->color.registers[1] = ParseExpression( src );
1465                         continue;
1466                 }
1467                 if ( !token.Icmp( "blue" ) ) {
1468                         ss->color.registers[2] = ParseExpression( src );
1469                         continue;
1470                 }
1471                 if ( !token.Icmp( "alpha" ) ) {
1472                         ss->color.registers[3] = ParseExpression( src );
1473                         continue;
1474                 }
1475                 if ( !token.Icmp( "rgb" ) ) {
1476                         ss->color.registers[0] = ss->color.registers[1] = 
1477                                 ss->color.registers[2] = ParseExpression( src );
1478                         continue;
1479                 }
1480                 if ( !token.Icmp( "rgba" ) ) {
1481                         ss->color.registers[0] = ss->color.registers[1] = 
1482                                 ss->color.registers[2] = ss->color.registers[3] = ParseExpression( src );
1483                         continue;
1484                 }
1485
1486                 if ( !token.Icmp( "if" ) ) {
1487                         ss->conditionRegister = ParseExpression( src );
1488                         continue;
1489                 }
1490                 if ( !token.Icmp( "program" ) ) {
1491                         if ( src.ReadTokenOnLine( &token ) ) {
1492                                 newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() );
1493                                 newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() );
1494                         }
1495                         continue;
1496                 }
1497                 if ( !token.Icmp( "fragmentProgram" ) ) {
1498                         if ( src.ReadTokenOnLine( &token ) ) {
1499                                 newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() );
1500                         }
1501                         continue;
1502                 }
1503                 if ( !token.Icmp( "vertexProgram" ) ) {
1504                         if ( src.ReadTokenOnLine( &token ) ) {
1505                                 newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() );
1506                         }
1507                         continue;
1508                 }
1509                 if ( !token.Icmp( "megaTexture" ) ) {
1510                         if ( src.ReadTokenOnLine( &token ) ) {
1511                                 newStage.megaTexture = new idMegaTexture;
1512                                 if ( !newStage.megaTexture->InitFromMegaFile( token.c_str() ) ) {
1513                                         delete newStage.megaTexture;
1514                                         SetMaterialFlag( MF_DEFAULTED );
1515                                         continue;
1516                                 }
1517                                 newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "megaTexture.vfp" );
1518                                 newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "megaTexture.vfp" );
1519                                 continue;
1520                         }
1521                 }
1522
1523
1524                 if ( !token.Icmp( "vertexParm" ) ) {
1525                         ParseVertexParm( src, &newStage );
1526                         continue;
1527                 }
1528
1529                 if (  !token.Icmp( "fragmentMap" ) ) {  
1530                         ParseFragmentMap( src, &newStage );
1531                         continue;
1532                 }
1533
1534
1535                 common->Warning( "unknown token '%s' in material '%s'", token.c_str(), GetName() );
1536                 SetMaterialFlag( MF_DEFAULTED );
1537                 return;
1538         }
1539
1540
1541         // if we are using newStage, allocate a copy of it
1542         if ( newStage.fragmentProgram || newStage.vertexProgram ) {
1543                 ss->newStage = (newShaderStage_t *)Mem_Alloc( sizeof( newStage ) );
1544                 *(ss->newStage) = newStage;
1545         }
1546
1547         // successfully parsed a stage
1548         numStages++;
1549
1550         // select a compressed depth based on what the stage is
1551         if ( td == TD_DEFAULT ) {
1552                 switch( ss->lighting ) {
1553                 case SL_BUMP:
1554                         td = TD_BUMP;
1555                         break;
1556                 case SL_DIFFUSE:
1557                         td = TD_DIFFUSE;
1558                         break;
1559                 case SL_SPECULAR:
1560                         td = TD_SPECULAR;
1561                         break;
1562                 default:
1563                         break;
1564                 }
1565         }
1566
1567         // now load the image with all the parms we parsed
1568         if ( imageName[0] ) {
1569                 ts->image = globalImages->ImageFromFile( imageName, tf, allowPicmip, trp, td, cubeMap );
1570                 if ( !ts->image ) {
1571                         ts->image = globalImages->defaultImage;
1572                 }
1573         } else if ( !ts->cinematic && !ts->dynamic && !ss->newStage ) {
1574                 common->Warning( "material '%s' had stage with no image", GetName() );
1575                 ts->image = globalImages->defaultImage;
1576         }
1577 }
1578
1579 /*
1580 ===============
1581 idMaterial::ParseDeform
1582 ===============
1583 */
1584 void idMaterial::ParseDeform( idLexer &src ) {
1585         idToken token;
1586
1587         if ( !src.ExpectAnyToken( &token ) ) {
1588                 return;
1589         }
1590
1591         if ( !token.Icmp( "sprite" ) ) {
1592                 deform = DFRM_SPRITE;
1593                 cullType = CT_TWO_SIDED;
1594                 SetMaterialFlag( MF_NOSHADOWS );
1595                 return;
1596         }
1597         if ( !token.Icmp( "tube" ) ) {
1598                 deform = DFRM_TUBE;
1599                 cullType = CT_TWO_SIDED;
1600                 SetMaterialFlag( MF_NOSHADOWS );
1601                 return;
1602         }
1603         if ( !token.Icmp( "flare" ) ) {
1604                 deform = DFRM_FLARE;
1605                 cullType = CT_TWO_SIDED;
1606                 deformRegisters[0] = ParseExpression( src );
1607                 SetMaterialFlag( MF_NOSHADOWS );
1608                 return;
1609         }
1610         if ( !token.Icmp( "expand" ) ) {
1611                 deform = DFRM_EXPAND;
1612                 deformRegisters[0] = ParseExpression( src );
1613                 return;
1614         }
1615         if ( !token.Icmp( "move" ) ) {
1616                 deform = DFRM_MOVE;
1617                 deformRegisters[0] = ParseExpression( src );
1618                 return;
1619         }
1620         if ( !token.Icmp( "turbulent" ) ) {
1621                 deform = DFRM_TURB;
1622
1623                 if ( !src.ExpectAnyToken( &token ) ) {
1624                         src.Warning( "deform particle missing particle name" );
1625                         SetMaterialFlag( MF_DEFAULTED );
1626                         return;
1627                 }
1628                 deformDecl = declManager->FindType( DECL_TABLE, token.c_str(), true );
1629
1630                 deformRegisters[0] = ParseExpression( src );
1631                 deformRegisters[1] = ParseExpression( src );
1632                 deformRegisters[2] = ParseExpression( src );
1633                 return;
1634         }
1635         if ( !token.Icmp( "eyeBall" ) ) {
1636                 deform = DFRM_EYEBALL;
1637                 return;
1638         }
1639         if ( !token.Icmp( "particle" ) ) {
1640                 deform = DFRM_PARTICLE;
1641                 if ( !src.ExpectAnyToken( &token ) ) {
1642                         src.Warning( "deform particle missing particle name" );
1643                         SetMaterialFlag( MF_DEFAULTED );
1644                         return;
1645                 }
1646                 deformDecl = declManager->FindType( DECL_PARTICLE, token.c_str(), true );
1647                 return;
1648         }
1649         if ( !token.Icmp( "particle2" ) ) {
1650                 deform = DFRM_PARTICLE2;
1651                 if ( !src.ExpectAnyToken( &token ) ) {
1652                         src.Warning( "deform particle missing particle name" );
1653                         SetMaterialFlag( MF_DEFAULTED );
1654                         return;
1655                 }
1656                 deformDecl = declManager->FindType( DECL_PARTICLE, token.c_str(), true );
1657                 return;
1658         }
1659         src.Warning( "Bad deform type '%s'", token.c_str() );
1660         SetMaterialFlag( MF_DEFAULTED );
1661 }
1662
1663
1664 /*
1665 ==============
1666 idMaterial::AddImplicitStages
1667
1668 If a material has diffuse or specular stages without any
1669 bump stage, add an implicit _flat bumpmap stage.
1670
1671 If a material has a bump stage but no diffuse or specular
1672 stage, add a _white diffuse stage.
1673
1674 It is valid to have either a diffuse or specular without the other.
1675
1676 It is valid to have a reflection map and a bump map for bumpy reflection
1677 ==============
1678 */
1679 void idMaterial::AddImplicitStages( const textureRepeat_t trpDefault /* = TR_REPEAT  */ ) {
1680         char    buffer[1024];
1681         idLexer         newSrc;
1682         bool hasDiffuse = false;
1683         bool hasSpecular = false;
1684         bool hasBump = false;
1685         bool hasReflection = false;
1686
1687         for ( int i = 0 ; i < numStages ; i++ ) {
1688                 if ( pd->parseStages[i].lighting == SL_BUMP ) {
1689                         hasBump = true;
1690                 }
1691                 if ( pd->parseStages[i].lighting == SL_DIFFUSE ) {
1692                         hasDiffuse = true;
1693                 }
1694                 if ( pd->parseStages[i].lighting == SL_SPECULAR ) {
1695                         hasSpecular = true;
1696                 }
1697                 if ( pd->parseStages[i].texture.texgen == TG_REFLECT_CUBE ) {
1698                         hasReflection = true;
1699                 }
1700         }
1701
1702         // if it doesn't have an interaction at all, don't add anything
1703         if ( !hasBump && !hasDiffuse && !hasSpecular ) {
1704                 return;
1705         }
1706
1707         if ( numStages == MAX_SHADER_STAGES ) {
1708                 return;
1709         }
1710
1711         if ( !hasBump ) {
1712                 idStr::snPrintf( buffer, sizeof( buffer ), "blend bumpmap\nmap _flat\n}\n" );
1713                 newSrc.LoadMemory( buffer, strlen(buffer), "bumpmap" );
1714                 newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
1715                 ParseStage( newSrc, trpDefault );
1716                 newSrc.FreeSource();
1717         }
1718
1719         if ( !hasDiffuse && !hasSpecular && !hasReflection ) {
1720                 idStr::snPrintf( buffer, sizeof( buffer ), "blend diffusemap\nmap _white\n}\n" );
1721                 newSrc.LoadMemory( buffer, strlen(buffer), "diffusemap" );
1722                 newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
1723                 ParseStage( newSrc, trpDefault );
1724                 newSrc.FreeSource();
1725         }
1726
1727 }
1728
1729 /*
1730 ===============
1731 idMaterial::SortInteractionStages
1732
1733 The renderer expects bump, then diffuse, then specular
1734 There can be multiple bump maps, followed by additional
1735 diffuse and specular stages, which allows cross-faded bump mapping.
1736
1737 Ambient stages can be interspersed anywhere, but they are
1738 ignored during interactions, and all the interaction
1739 stages are ignored during ambient drawing.
1740 ===============
1741 */
1742 void idMaterial::SortInteractionStages() {
1743         int             j;
1744
1745         for ( int i = 0 ; i < numStages ; i = j ) {
1746                 // find the next bump map
1747                 for ( j = i + 1 ; j < numStages ; j++ ) {
1748                         if ( pd->parseStages[j].lighting == SL_BUMP ) {
1749                                 // if the very first stage wasn't a bumpmap,
1750                                 // this bumpmap is part of the first group
1751                                 if ( pd->parseStages[i].lighting != SL_BUMP ) {
1752                                         continue;
1753                                 }
1754                                 break;
1755                         }
1756                 }
1757
1758                 // bubble sort everything bump / diffuse / specular
1759                 for ( int l = 1 ; l < j-i ; l++ ) {
1760                         for ( int k = i ; k < j-l ; k++ ) {
1761                                 if ( pd->parseStages[k].lighting > pd->parseStages[k+1].lighting ) {
1762                                         shaderStage_t   temp;
1763
1764                                         temp = pd->parseStages[k];
1765                                         pd->parseStages[k] = pd->parseStages[k+1];
1766                                         pd->parseStages[k+1] = temp;
1767                                 }
1768                         }
1769                 }
1770         }
1771 }
1772
1773 /*
1774 =================
1775 idMaterial::ParseMaterial
1776
1777 The current text pointer is at the explicit text definition of the
1778 Parse it into the global material variable. Later functions will optimize it.
1779
1780 If there is any error during parsing, defaultShader will be set.
1781 =================
1782 */
1783 void idMaterial::ParseMaterial( idLexer &src ) {
1784         idToken         token;
1785         int                     s;
1786         char            buffer[1024];
1787         const char      *str;
1788         idLexer         newSrc;
1789         int                     i;
1790
1791         s = 0;
1792
1793         numOps = 0;
1794         numRegisters = EXP_REG_NUM_PREDEFINED;  // leave space for the parms to be copied in
1795         for ( i = 0 ; i < numRegisters ; i++ ) {
1796                 pd->registerIsTemporary[i] = true;              // they aren't constants that can be folded
1797         }
1798
1799         numStages = 0;
1800
1801         textureRepeat_t trpDefault = TR_REPEAT;         // allow a global setting for repeat
1802
1803         while ( 1 ) {
1804                 if ( TestMaterialFlag( MF_DEFAULTED ) ) {       // we have a parse error
1805                         return;
1806                 }
1807                 if ( !src.ExpectAnyToken( &token ) ) {
1808                         SetMaterialFlag( MF_DEFAULTED );
1809                         return;
1810                 }
1811
1812                 // end of material definition
1813                 if ( token == "}" ) {
1814                         break;
1815                 }
1816                 else if ( !token.Icmp( "qer_editorimage") ) {
1817                         src.ReadTokenOnLine( &token );
1818                         editorImageName = token.c_str();
1819                         src.SkipRestOfLine();
1820                         continue;
1821                 }
1822                 // description
1823                 else if ( !token.Icmp( "description") ) {
1824                         src.ReadTokenOnLine( &token );
1825                         desc = token.c_str();
1826                         continue;
1827                 }
1828                 // check for the surface / content bit flags
1829                 else if ( CheckSurfaceParm( &token ) ) {
1830                         continue;
1831                 }
1832
1833
1834                 // polygonOffset
1835                 else if ( !token.Icmp( "polygonOffset" ) ) {
1836                         SetMaterialFlag( MF_POLYGONOFFSET );
1837                         if ( !src.ReadTokenOnLine( &token ) ) {
1838                                 polygonOffset = 1;
1839                                 continue;
1840                         }
1841                         // explict larger (or negative) offset
1842                         polygonOffset = token.GetFloatValue();
1843                         continue;
1844                 }
1845                 // noshadow
1846                 else if ( !token.Icmp( "noShadows" ) ) {
1847                         SetMaterialFlag( MF_NOSHADOWS );
1848                         continue;
1849                 }
1850                 else if ( !token.Icmp( "suppressInSubview" ) ) {
1851                         suppressInSubview = true;
1852                         continue;
1853                 }
1854                 else if ( !token.Icmp( "portalSky" ) ) {
1855                         portalSky = true;
1856                         continue;
1857                 }
1858                 // noSelfShadow
1859                 else if ( !token.Icmp( "noSelfShadow" ) ) {
1860                         SetMaterialFlag( MF_NOSELFSHADOW );
1861                         continue;
1862                 }
1863                 // noPortalFog
1864                 else if ( !token.Icmp( "noPortalFog" ) ) {
1865                         SetMaterialFlag( MF_NOPORTALFOG );
1866                         continue;
1867                 }
1868                 // forceShadows allows nodraw surfaces to cast shadows
1869                 else if ( !token.Icmp( "forceShadows" ) ) {
1870                         SetMaterialFlag( MF_FORCESHADOWS );
1871                         continue;
1872                 }
1873                 // overlay / decal suppression
1874                 else if ( !token.Icmp( "noOverlays" ) ) {
1875                         allowOverlays = false;
1876                         continue;
1877                 }
1878                 // moster blood overlay forcing for alpha tested or translucent surfaces
1879                 else if ( !token.Icmp( "forceOverlays" ) ) {
1880                         pd->forceOverlays = true;
1881                         continue;
1882                 }
1883                 // translucent
1884                 else if ( !token.Icmp( "translucent" ) ) {
1885                         coverage = MC_TRANSLUCENT;
1886                         continue;
1887                 }
1888                 // global zero clamp
1889                 else if ( !token.Icmp( "zeroclamp" ) ) {
1890                         trpDefault = TR_CLAMP_TO_ZERO;
1891                         continue;
1892                 }
1893                 // global clamp
1894                 else if ( !token.Icmp( "clamp" ) ) {
1895                         trpDefault = TR_CLAMP;
1896                         continue;
1897                 }
1898                 // global clamp
1899                 else if ( !token.Icmp( "alphazeroclamp" ) ) {
1900                         trpDefault = TR_CLAMP_TO_ZERO;
1901                         continue;
1902                 }
1903                 // forceOpaque is used for skies-behind-windows
1904                 else if ( !token.Icmp( "forceOpaque" ) ) {
1905                         coverage = MC_OPAQUE;
1906                         continue;
1907                 }
1908                 // twoSided
1909                 else if ( !token.Icmp( "twoSided" ) ) {
1910                         cullType = CT_TWO_SIDED;
1911                         // twoSided implies no-shadows, because the shadow
1912                         // volume would be coplanar with the surface, giving depth fighting
1913                         // we could make this no-self-shadows, but it may be more important
1914                         // to receive shadows from no-self-shadow monsters
1915                         SetMaterialFlag( MF_NOSHADOWS );
1916                 }
1917                 // backSided
1918                 else if ( !token.Icmp( "backSided" ) ) {
1919                         cullType = CT_BACK_SIDED;
1920                         // the shadow code doesn't handle this, so just disable shadows.
1921                         // We could fix this in the future if there was a need.
1922                         SetMaterialFlag( MF_NOSHADOWS );
1923                 }
1924                 // foglight
1925                 else if ( !token.Icmp( "fogLight" ) ) {
1926                         fogLight = true;
1927                         continue;
1928                 }
1929                 // blendlight
1930                 else if ( !token.Icmp( "blendLight" ) ) {
1931                         blendLight = true;
1932                         continue;
1933                 }
1934                 // ambientLight
1935                 else if ( !token.Icmp( "ambientLight" ) ) {
1936                         ambientLight = true;
1937                         continue;
1938                 }
1939                 // mirror
1940                 else if ( !token.Icmp( "mirror" ) ) {
1941                         sort = SS_SUBVIEW;
1942                         coverage = MC_OPAQUE;
1943                         continue;
1944                 }
1945                 // noFog
1946                 else if ( !token.Icmp( "noFog" ) ) {
1947                         noFog = true;
1948                         continue;
1949                 }
1950                 // unsmoothedTangents
1951                 else if ( !token.Icmp( "unsmoothedTangents" ) ) {
1952                         unsmoothedTangents = true;
1953                         continue;
1954                 }
1955                 // lightFallofImage <imageprogram>
1956                 // specifies the image to use for the third axis of projected
1957                 // light volumes
1958                 else if ( !token.Icmp( "lightFalloffImage" ) ) {
1959                         str = R_ParsePastImageProgram( src );
1960                         idStr   copy;
1961
1962                         copy = str;     // so other things don't step on it
1963                         lightFalloffImage = globalImages->ImageFromFile( copy, TF_DEFAULT, false, TR_CLAMP /* TR_CLAMP_TO_ZERO */, TD_DEFAULT );
1964                         continue;
1965                 }
1966                 // guisurf <guifile> | guisurf entity
1967                 // an entity guisurf must have an idUserInterface
1968                 // specified in the renderEntity
1969                 else if ( !token.Icmp( "guisurf" ) ) {
1970                         src.ReadTokenOnLine( &token );
1971                         if ( !token.Icmp( "entity" ) ) {
1972                                 entityGui = 1;
1973                         } else if ( !token.Icmp( "entity2" ) ) {
1974                                 entityGui = 2;
1975                         } else if ( !token.Icmp( "entity3" ) ) {
1976                                 entityGui = 3;
1977                         } else {
1978                                 gui = uiManager->FindGui( token.c_str(), true );
1979                         }
1980                         continue;
1981                 }
1982                 // sort
1983                 else if ( !token.Icmp( "sort" ) ) {
1984                         ParseSort( src );
1985                         continue;
1986                 }
1987                 // spectrum <integer>
1988                 else if ( !token.Icmp( "spectrum" ) ) {
1989                         src.ReadTokenOnLine( &token );
1990                         spectrum = atoi( token.c_str() );
1991                         continue;
1992                 }
1993                 // deform < sprite | tube | flare >
1994                 else if ( !token.Icmp( "deform" ) ) {
1995                         ParseDeform( src );
1996                         continue;
1997                 }
1998                 // decalInfo <staySeconds> <fadeSeconds> ( <start rgb> ) ( <end rgb> )
1999                 else if ( !token.Icmp( "decalInfo" ) ) {
2000                         ParseDecalInfo( src );
2001                         continue;
2002                 }
2003                 // renderbump <args...>
2004                 else if ( !token.Icmp( "renderbump") ) {
2005                         src.ParseRestOfLine( renderBump );
2006                         continue;
2007                 }
2008                 // diffusemap for stage shortcut
2009                 else if ( !token.Icmp( "diffusemap" ) ) {
2010                         str = R_ParsePastImageProgram( src );
2011                         idStr::snPrintf( buffer, sizeof( buffer ), "blend diffusemap\nmap %s\n}\n", str );
2012                         newSrc.LoadMemory( buffer, strlen(buffer), "diffusemap" );
2013                         newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
2014                         ParseStage( newSrc, trpDefault );
2015                         newSrc.FreeSource();
2016                         continue;
2017                 }
2018                 // specularmap for stage shortcut
2019                 else if ( !token.Icmp( "specularmap" ) ) {
2020                         str = R_ParsePastImageProgram( src );
2021                         idStr::snPrintf( buffer, sizeof( buffer ), "blend specularmap\nmap %s\n}\n", str );
2022                         newSrc.LoadMemory( buffer, strlen(buffer), "specularmap" );
2023                         newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
2024                         ParseStage( newSrc, trpDefault );
2025                         newSrc.FreeSource();
2026                         continue;
2027                 }
2028                 // normalmap for stage shortcut
2029                 else if ( !token.Icmp( "bumpmap" ) ) {
2030                         str = R_ParsePastImageProgram( src );
2031                         idStr::snPrintf( buffer, sizeof( buffer ), "blend bumpmap\nmap %s\n}\n", str );
2032                         newSrc.LoadMemory( buffer, strlen(buffer), "bumpmap" );
2033                         newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
2034                         ParseStage( newSrc, trpDefault );
2035                         newSrc.FreeSource();
2036                         continue;
2037                 }
2038                 // DECAL_MACRO for backwards compatibility with the preprocessor macros
2039                 else if ( !token.Icmp( "DECAL_MACRO" ) ) {
2040                         // polygonOffset
2041                         SetMaterialFlag( MF_POLYGONOFFSET );
2042                         polygonOffset = 1;
2043
2044                         // discrete
2045                         surfaceFlags |= SURF_DISCRETE;
2046                         contentFlags &= ~CONTENTS_SOLID;
2047
2048                         // sort decal
2049                         sort = SS_DECAL;
2050
2051                         // noShadows
2052                         SetMaterialFlag( MF_NOSHADOWS );
2053                         continue;
2054                 }
2055                 else if ( token == "{" ) {
2056                         // create the new stage
2057                         ParseStage( src, trpDefault );
2058                         continue;
2059                 }
2060                 else {
2061                         common->Warning( "unknown general material parameter '%s' in '%s'", token.c_str(), GetName() );
2062                         SetMaterialFlag( MF_DEFAULTED );
2063                         return;
2064                 }
2065         }
2066
2067         // add _flat or _white stages if needed
2068         AddImplicitStages();
2069
2070         // order the diffuse / bump / specular stages properly
2071         SortInteractionStages();
2072
2073         // if we need to do anything with normals (lighting or environment mapping)
2074         // and two sided lighting was asked for, flag
2075         // shouldCreateBackSides() and change culling back to single sided,
2076         // so we get proper tangent vectors on both sides
2077
2078         // we can't just call ReceivesLighting(), because the stages are still
2079         // in temporary form
2080         if ( cullType == CT_TWO_SIDED ) {
2081                 for ( i = 0 ; i < numStages ; i++ ) {
2082                         if ( pd->parseStages[i].lighting != SL_AMBIENT || pd->parseStages[i].texture.texgen != TG_EXPLICIT ) {
2083                                 if ( cullType == CT_TWO_SIDED ) {
2084                                         cullType = CT_FRONT_SIDED;
2085                                         shouldCreateBackSides = true;
2086                                 }
2087                                 break;
2088                         }
2089                 }
2090         }
2091
2092         // currently a surface can only have one unique texgen for all the stages on old hardware
2093         texgen_t firstGen = TG_EXPLICIT;
2094         for ( i = 0; i < numStages; i++ ) {
2095                 if ( pd->parseStages[i].texture.texgen != TG_EXPLICIT ) {
2096                         if ( firstGen == TG_EXPLICIT ) {
2097                                 firstGen = pd->parseStages[i].texture.texgen;
2098                         } else if ( firstGen != pd->parseStages[i].texture.texgen ) {
2099                                 common->Warning( "material '%s' has multiple stages with a texgen", GetName() );
2100                                 break;
2101                         }
2102                 }
2103         }
2104 }
2105
2106 /*
2107 =========================
2108 idMaterial::SetGui
2109 =========================
2110 */
2111 void idMaterial::SetGui( const char *_gui ) const {
2112         gui = uiManager->FindGui( _gui, true, false, true );
2113 }
2114
2115 /*
2116 =========================
2117 idMaterial::Parse
2118
2119 Parses the current material definition and finds all necessary images.
2120 =========================
2121 */
2122 bool idMaterial::Parse( const char *text, const int textLength ) {
2123         idLexer src;
2124         idToken token;
2125         mtrParsingData_t parsingData;
2126
2127         src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
2128         src.SetFlags( DECL_LEXER_FLAGS );
2129         src.SkipUntilString( "{" );
2130
2131         // reset to the unparsed state
2132         CommonInit();
2133
2134         memset( &parsingData, 0, sizeof( parsingData ) );
2135
2136         pd = &parsingData;      // this is only valid during parse
2137
2138         // parse it
2139         ParseMaterial( src );
2140
2141         // if we are doing an fs_copyfiles, also reference the editorImage
2142         if ( cvarSystem->GetCVarInteger( "fs_copyFiles" ) ) {
2143                 GetEditorImage();
2144         }
2145
2146         //
2147         // count non-lit stages
2148         numAmbientStages = 0;
2149         int i;
2150         for ( i = 0 ; i < numStages ; i++ ) {
2151                 if ( pd->parseStages[i].lighting == SL_AMBIENT ) {
2152                         numAmbientStages++;
2153                 }
2154         }
2155
2156         // see if there is a subview stage
2157         if ( sort == SS_SUBVIEW ) {
2158                 hasSubview = true;
2159         } else {
2160                 hasSubview = false;
2161                 for ( i = 0 ; i < numStages ; i++ ) {
2162                         if ( pd->parseStages[i].texture.dynamic ) {
2163                                 hasSubview = true;
2164                         }
2165                 }
2166         }
2167
2168         // automatically determine coverage if not explicitly set
2169         if ( coverage == MC_BAD ) {
2170                 // automatically set MC_TRANSLUCENT if we don't have any interaction stages and 
2171                 // the first stage is blended and not an alpha test mask or a subview
2172                 if ( !numStages ) {
2173                         // non-visible
2174                         coverage = MC_TRANSLUCENT;
2175                 } else if ( numStages != numAmbientStages ) {
2176                         // we have an interaction draw
2177                         coverage = MC_OPAQUE;
2178                 } else if ( 
2179                         ( pd->parseStages[0].drawStateBits & GLS_DSTBLEND_BITS ) != GLS_DSTBLEND_ZERO ||
2180                         ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_DST_COLOR ||
2181                         ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_ONE_MINUS_DST_COLOR ||
2182                         ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_DST_ALPHA ||
2183                         ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_ONE_MINUS_DST_ALPHA
2184                         ) {
2185                         // blended with the destination
2186                                 coverage = MC_TRANSLUCENT;
2187                 } else {
2188                         coverage = MC_OPAQUE;
2189                 }
2190         }
2191
2192         // translucent automatically implies noshadows
2193         if ( coverage == MC_TRANSLUCENT ) {
2194                 SetMaterialFlag( MF_NOSHADOWS );
2195         } else {
2196                 // mark the contents as opaque
2197                 contentFlags |= CONTENTS_OPAQUE;
2198         }
2199
2200         // if we are translucent, draw with an alpha in the editor
2201         if ( coverage == MC_TRANSLUCENT ) {
2202                 editorAlpha = 0.5;
2203         } else {
2204                 editorAlpha = 1.0;
2205         }
2206
2207         // the sorts can make reasonable defaults
2208         if ( sort == SS_BAD ) {
2209                 if ( TestMaterialFlag(MF_POLYGONOFFSET) ) {
2210                         sort = SS_DECAL;
2211                 } else if ( coverage == MC_TRANSLUCENT ) {
2212                         sort = SS_MEDIUM;
2213                 } else {
2214                         sort = SS_OPAQUE;
2215                 }
2216         }
2217
2218         // anything that references _currentRender will automatically get sort = SS_POST_PROCESS
2219         // and coverage = MC_TRANSLUCENT
2220
2221         for ( i = 0 ; i < numStages ; i++ ) {
2222                 shaderStage_t   *pStage = &pd->parseStages[i];
2223                 if ( pStage->texture.image == globalImages->currentRenderImage ) {
2224                         if ( sort != SS_PORTAL_SKY ) {
2225                                 sort = SS_POST_PROCESS;
2226                                 coverage = MC_TRANSLUCENT;
2227                         }
2228                         break;
2229                 }
2230                 if ( pStage->newStage ) {
2231                         for ( int j = 0 ; j < pStage->newStage->numFragmentProgramImages ; j++ ) {
2232                                 if ( pStage->newStage->fragmentProgramImages[j] == globalImages->currentRenderImage ) {
2233                                         if ( sort != SS_PORTAL_SKY ) {
2234                                                 sort = SS_POST_PROCESS;
2235                                                 coverage = MC_TRANSLUCENT;
2236                                         }
2237                                         i = numStages;
2238                                         break;
2239                                 }
2240                         }
2241                 }
2242         }
2243
2244         // set the drawStateBits depth flags
2245         for ( i = 0 ; i < numStages ; i++ ) {
2246                 shaderStage_t   *pStage = &pd->parseStages[i];
2247                 if ( sort == SS_POST_PROCESS ) {
2248                         // post-process effects fill the depth buffer as they draw, so only the
2249                         // topmost post-process effect is rendered
2250                         pStage->drawStateBits |= GLS_DEPTHFUNC_LESS;
2251                 } else if ( coverage == MC_TRANSLUCENT || pStage->ignoreAlphaTest ) {
2252                         // translucent surfaces can extend past the exactly marked depth buffer
2253                         pStage->drawStateBits |= GLS_DEPTHFUNC_LESS | GLS_DEPTHMASK;
2254                 } else {
2255                         // opaque and perforated surfaces must exactly match the depth buffer,
2256                         // which gets alpha test correct
2257                         pStage->drawStateBits |= GLS_DEPTHFUNC_EQUAL | GLS_DEPTHMASK;
2258                 }
2259         }
2260
2261         // determine if this surface will accept overlays / decals
2262
2263         if ( pd->forceOverlays ) {
2264                 // explicitly flaged in material definition
2265                 allowOverlays = true;
2266         } else {
2267                 if ( !IsDrawn() ) {
2268                         allowOverlays = false;
2269                 }
2270                 if ( Coverage() != MC_OPAQUE ) {
2271                         allowOverlays = false;
2272                 }
2273                 if ( GetSurfaceFlags() & SURF_NOIMPACT ) {
2274                         allowOverlays = false;
2275                 }
2276         }
2277
2278         // add a tiny offset to the sort orders, so that different materials
2279         // that have the same sort value will at least sort consistantly, instead
2280         // of flickering back and forth
2281 /* this messed up in-game guis
2282         if ( sort != SS_SUBVIEW ) {
2283                 int     hash, l;
2284
2285                 l = name.Length();
2286                 hash = 0;
2287                 for ( int i = 0 ; i < l ; i++ ) {
2288                         hash ^= name[i];
2289                 }
2290                 sort += hash * 0.01;
2291         }
2292 */
2293
2294         if (numStages) {
2295                 stages = (shaderStage_t *)R_StaticAlloc( numStages * sizeof( stages[0] ) );
2296                 memcpy( stages, pd->parseStages, numStages * sizeof( stages[0] ) );
2297         }
2298
2299         if ( numOps ) {
2300                 ops = (expOp_t *)R_StaticAlloc( numOps * sizeof( ops[0] ) );
2301                 memcpy( ops, pd->shaderOps, numOps * sizeof( ops[0] ) );
2302         }
2303
2304         if ( numRegisters ) {
2305                 expressionRegisters = (float *)R_StaticAlloc( numRegisters * sizeof( expressionRegisters[0] ) );
2306                 memcpy( expressionRegisters, pd->shaderRegisters, numRegisters * sizeof( expressionRegisters[0] ) );
2307         }
2308
2309         // see if the registers are completely constant, and don't need to be evaluated
2310         // per-surface
2311         CheckForConstantRegisters();
2312
2313         pd = NULL;      // the pointer will be invalid after exiting this function
2314
2315         // finish things up
2316         if ( TestMaterialFlag( MF_DEFAULTED ) ) {
2317                 MakeDefault();
2318                 return false;
2319         }
2320         return true;
2321 }
2322
2323 /*
2324 ===================
2325 idMaterial::Print
2326 ===================
2327 */
2328 char *opNames[] = {
2329         "OP_TYPE_ADD",
2330         "OP_TYPE_SUBTRACT",
2331         "OP_TYPE_MULTIPLY",
2332         "OP_TYPE_DIVIDE",
2333         "OP_TYPE_MOD",
2334         "OP_TYPE_TABLE",
2335         "OP_TYPE_GT",
2336         "OP_TYPE_GE",
2337         "OP_TYPE_LT",
2338         "OP_TYPE_LE",
2339         "OP_TYPE_EQ",
2340         "OP_TYPE_NE",
2341         "OP_TYPE_AND",
2342         "OP_TYPE_OR"
2343 };
2344
2345 void idMaterial::Print() const {
2346         int                     i;
2347
2348         for ( i = EXP_REG_NUM_PREDEFINED ; i < GetNumRegisters() ; i++ ) {
2349                 common->Printf( "register %i: %f\n", i, expressionRegisters[i] );
2350         }
2351         common->Printf( "\n" );
2352         for ( i = 0 ; i < numOps ; i++ ) {
2353                 const expOp_t *op = &ops[i];
2354                 if ( op->opType == OP_TYPE_TABLE ) {
2355                         common->Printf( "%i = %s[ %i ]\n", op->c, declManager->DeclByIndex( DECL_TABLE, op->a )->GetName(), op->b );
2356                 } else {
2357                         common->Printf( "%i = %i %s %i\n", op->c, op->a, opNames[ op->opType ], op->b );
2358                 }
2359         }
2360 }
2361
2362 /*
2363 ===============
2364 idMaterial::Save
2365 ===============
2366 */
2367 bool idMaterial::Save( const char *fileName ) {
2368         return ReplaceSourceFileText();
2369 }
2370
2371 /*
2372 ===============
2373 idMaterial::AddReference
2374 ===============
2375 */
2376 void idMaterial::AddReference() {
2377         refCount++;
2378
2379         for ( int i = 0; i < numStages; i++ ) {
2380                 shaderStage_t *s = &stages[i];
2381
2382                 if ( s->texture.image ) {
2383                         s->texture.image->AddReference();
2384                 }
2385         }
2386 }
2387
2388 /*
2389 ===============
2390 idMaterial::EvaluateRegisters
2391
2392 Parameters are taken from the localSpace and the renderView,
2393 then all expressions are evaluated, leaving the material registers
2394 set to their apropriate values.
2395 ===============
2396 */
2397 void idMaterial::EvaluateRegisters( float *registers, const float shaderParms[MAX_ENTITY_SHADER_PARMS],
2398                                                                         const viewDef_t *view, idSoundEmitter *soundEmitter ) const {
2399         int             i, b;
2400         expOp_t *op;
2401
2402         // copy the material constants
2403         for ( i = EXP_REG_NUM_PREDEFINED ; i < numRegisters ; i++ ) {
2404                 registers[i] = expressionRegisters[i];
2405         }
2406
2407         // copy the local and global parameters
2408         registers[EXP_REG_TIME] = view->floatTime;
2409         registers[EXP_REG_PARM0] = shaderParms[0];
2410         registers[EXP_REG_PARM1] = shaderParms[1];
2411         registers[EXP_REG_PARM2] = shaderParms[2];
2412         registers[EXP_REG_PARM3] = shaderParms[3];
2413         registers[EXP_REG_PARM4] = shaderParms[4];
2414         registers[EXP_REG_PARM5] = shaderParms[5];
2415         registers[EXP_REG_PARM6] = shaderParms[6];
2416         registers[EXP_REG_PARM7] = shaderParms[7];
2417         registers[EXP_REG_PARM8] = shaderParms[8];
2418         registers[EXP_REG_PARM9] = shaderParms[9];
2419         registers[EXP_REG_PARM10] = shaderParms[10];
2420         registers[EXP_REG_PARM11] = shaderParms[11];
2421         registers[EXP_REG_GLOBAL0] = view->renderView.shaderParms[0];
2422         registers[EXP_REG_GLOBAL1] = view->renderView.shaderParms[1];
2423         registers[EXP_REG_GLOBAL2] = view->renderView.shaderParms[2];
2424         registers[EXP_REG_GLOBAL3] = view->renderView.shaderParms[3];
2425         registers[EXP_REG_GLOBAL4] = view->renderView.shaderParms[4];
2426         registers[EXP_REG_GLOBAL5] = view->renderView.shaderParms[5];
2427         registers[EXP_REG_GLOBAL6] = view->renderView.shaderParms[6];
2428         registers[EXP_REG_GLOBAL7] = view->renderView.shaderParms[7];
2429
2430         op = ops;
2431         for ( i = 0 ; i < numOps ; i++, op++ ) {
2432                 switch( op->opType ) {
2433                 case OP_TYPE_ADD:
2434                         registers[op->c] = registers[op->a] + registers[op->b];
2435                         break;
2436                 case OP_TYPE_SUBTRACT:
2437                         registers[op->c] = registers[op->a] - registers[op->b];
2438                         break;
2439                 case OP_TYPE_MULTIPLY:
2440                         registers[op->c] = registers[op->a] * registers[op->b];
2441                         break;
2442                 case OP_TYPE_DIVIDE:
2443                         registers[op->c] = registers[op->a] / registers[op->b];
2444                         break;
2445                 case OP_TYPE_MOD:
2446                         b = (int)registers[op->b];
2447                         b = b != 0 ? b : 1;
2448                         registers[op->c] = (int)registers[op->a] % b;
2449                         break;
2450                 case OP_TYPE_TABLE:
2451                         {
2452                                 const idDeclTable *table = static_cast<const idDeclTable *>( declManager->DeclByIndex( DECL_TABLE, op->a ) );
2453                                 registers[op->c] = table->TableLookup( registers[op->b] );
2454                         }
2455                         break;
2456                 case OP_TYPE_SOUND:
2457                         if ( soundEmitter ) {
2458                                 registers[op->c] = soundEmitter->CurrentAmplitude();
2459                         } else {
2460                                 registers[op->c] = 0;
2461                         }
2462                         break;
2463                 case OP_TYPE_GT:
2464                         registers[op->c] = registers[ op->a ] > registers[op->b];
2465                         break;
2466                 case OP_TYPE_GE:
2467                         registers[op->c] = registers[ op->a ] >= registers[op->b];
2468                         break;
2469                 case OP_TYPE_LT:
2470                         registers[op->c] = registers[ op->a ] < registers[op->b];
2471                         break;
2472                 case OP_TYPE_LE:
2473                         registers[op->c] = registers[ op->a ] <= registers[op->b];
2474                         break;
2475                 case OP_TYPE_EQ:
2476                         registers[op->c] = registers[ op->a ] == registers[op->b];
2477                         break;
2478                 case OP_TYPE_NE:
2479                         registers[op->c] = registers[ op->a ] != registers[op->b];
2480                         break;
2481                 case OP_TYPE_AND:
2482                         registers[op->c] = registers[ op->a ] && registers[op->b];
2483                         break;
2484                 case OP_TYPE_OR:
2485                         registers[op->c] = registers[ op->a ] || registers[op->b];
2486                         break;
2487                 default:
2488                         common->FatalError( "R_EvaluateExpression: bad opcode" );
2489                 }
2490         }
2491
2492 }
2493
2494 /*
2495 =============
2496 idMaterial::Texgen
2497 =============
2498 */
2499 texgen_t idMaterial::Texgen() const {
2500         if ( stages ) {
2501                 for ( int i = 0; i < numStages; i++ ) {
2502                         if ( stages[ i ].texture.texgen != TG_EXPLICIT ) {
2503                                 return stages[ i ].texture.texgen;
2504                         }
2505                 }
2506         }
2507         
2508         return TG_EXPLICIT;
2509 }
2510
2511 /*
2512 =============
2513 idMaterial::GetImageWidth
2514 =============
2515 */
2516 int idMaterial::GetImageWidth( void ) const {
2517         assert( GetStage(0) && GetStage(0)->texture.image );
2518         return GetStage(0)->texture.image->uploadWidth;
2519 }
2520
2521 /*
2522 =============
2523 idMaterial::GetImageHeight
2524 =============
2525 */
2526 int idMaterial::GetImageHeight( void ) const {
2527         assert( GetStage(0) && GetStage(0)->texture.image );
2528         return GetStage(0)->texture.image->uploadHeight;
2529 }
2530
2531 /*
2532 =============
2533 idMaterial::CinematicLength
2534 =============
2535 */
2536 int     idMaterial::CinematicLength() const {
2537         if ( !stages || !stages[0].texture.cinematic ) {
2538                 return 0;
2539         }
2540         return stages[0].texture.cinematic->AnimationLength();
2541 }
2542
2543 /*
2544 =============
2545 idMaterial::UpdateCinematic
2546 =============
2547 */
2548 void idMaterial::UpdateCinematic( int time ) const {
2549         if ( !stages || !stages[0].texture.cinematic || !backEnd.viewDef ) {
2550                 return;
2551         }
2552         stages[0].texture.cinematic->ImageForTime( tr.primaryRenderView.time );
2553 }
2554
2555 /*
2556 =============
2557 idMaterial::CloseCinematic
2558 =============
2559 */
2560 void idMaterial::CloseCinematic( void ) const {
2561         for( int i = 0; i < numStages; i++ ) {
2562                 if ( stages[i].texture.cinematic ) {
2563                         stages[i].texture.cinematic->Close();
2564                         delete stages[i].texture.cinematic;
2565                         stages[i].texture.cinematic = NULL;
2566                 }
2567         }
2568 }
2569
2570 /*
2571 =============
2572 idMaterial::ResetCinematicTime
2573 =============
2574 */
2575 void idMaterial::ResetCinematicTime( int time ) const {
2576         for( int i = 0; i < numStages; i++ ) {
2577                 if ( stages[i].texture.cinematic ) {
2578                         stages[i].texture.cinematic->ResetTime( time );
2579                 }
2580         }
2581 }
2582
2583 /*
2584 =============
2585 idMaterial::ConstantRegisters
2586 =============
2587 */
2588 const float *idMaterial::ConstantRegisters() const {
2589         if ( !r_useConstantMaterials.GetBool() ) {
2590                 return NULL;
2591         }
2592         return constantRegisters;
2593 }
2594
2595 /*
2596 ==================
2597 idMaterial::CheckForConstantRegisters
2598
2599 As of 5/2/03, about half of the unique materials loaded on typical
2600 maps are constant, but 2/3 of the surface references are.
2601 This is probably an optimization of dubious value.
2602 ==================
2603 */
2604 static int      c_constant, c_variable;
2605 void idMaterial::CheckForConstantRegisters() {
2606         if ( !pd->registersAreConstant ) {
2607                 return;
2608         }
2609
2610         // evaluate the registers once, and save them 
2611         constantRegisters = (float *)R_ClearedStaticAlloc( GetNumRegisters() * sizeof( float ) );
2612
2613         float shaderParms[MAX_ENTITY_SHADER_PARMS];
2614         memset( shaderParms, 0, sizeof( shaderParms ) );
2615         viewDef_t       viewDef;
2616         memset( &viewDef, 0, sizeof( viewDef ) );
2617
2618         EvaluateRegisters( constantRegisters, shaderParms, &viewDef, 0 );
2619 }
2620
2621 /*
2622 ===================
2623 idMaterial::ImageName
2624 ===================
2625 */
2626 const char *idMaterial::ImageName( void ) const {
2627         if ( numStages == 0 ) {
2628                 return "_scratch";
2629         }
2630         idImage *image = stages[0].texture.image;
2631         if ( image ) {
2632                 return image->imgName;
2633         }
2634         return "_scratch";
2635 }
2636
2637 /*
2638 ===================
2639 idMaterial::SetImageClassifications
2640
2641 Just for image resource tracking.
2642 ===================
2643 */
2644 void idMaterial::SetImageClassifications( int tag ) const {
2645         for ( int i = 0 ; i < numStages ; i++ ) {
2646                 idImage *image = stages[i].texture.image;
2647                 if ( image ) {
2648                         image->SetClassification( tag );
2649                 }
2650         }
2651 }
2652
2653 /*
2654 =================
2655 idMaterial::Size
2656 =================
2657 */
2658 size_t idMaterial::Size( void ) const {
2659         return sizeof( idMaterial );
2660 }
2661
2662 /*
2663 ===================
2664 idMaterial::SetDefaultText
2665 ===================
2666 */
2667 bool idMaterial::SetDefaultText( void ) {
2668         // if there exists an image with the same name
2669         if ( 1 ) { //fileSystem->ReadFile( GetName(), NULL ) != -1 ) {
2670                 char generated[2048];
2671                 idStr::snPrintf( generated, sizeof( generated ), 
2672                                                 "material %s // IMPLICITLY GENERATED\n"
2673                                                 "{\n"
2674                                                 "{\n"
2675                                                 "blend blend\n"
2676                                                 "colored\n"
2677                                                 "map \"%s\"\n"
2678                                                 "clamp\n"
2679                                                 "}\n"
2680                                                 "}\n", GetName(), GetName() );
2681                 SetText( generated );
2682                 return true;
2683         } else {
2684                 return false;
2685         }
2686 }
2687
2688 /*
2689 ===================
2690 idMaterial::DefaultDefinition
2691 ===================
2692 */
2693 const char *idMaterial::DefaultDefinition() const {
2694         return
2695                 "{\n"
2696         "\t"    "{\n"
2697         "\t\t"          "blend\tblend\n"
2698         "\t\t"          "map\t\t_default\n"
2699         "\t"    "}\n"
2700                 "}";
2701 }
2702
2703
2704 /*
2705 ===================
2706 idMaterial::GetBumpStage
2707 ===================
2708 */
2709 const shaderStage_t *idMaterial::GetBumpStage( void ) const {
2710         for ( int i = 0 ; i < numStages ; i++ ) {
2711                 if ( stages[i].lighting == SL_BUMP ) {
2712                         return &stages[i];
2713                 }
2714         }
2715         return NULL;
2716 }
2717
2718 /*
2719 ===================
2720 idMaterial::ReloadImages
2721 ===================
2722 */
2723 void idMaterial::ReloadImages( bool force ) const
2724 {
2725         for ( int i = 0 ; i < numStages ; i++ ) {
2726                 if ( stages[i].newStage ) {
2727                         for ( int j = 0 ; j < stages[i].newStage->numFragmentProgramImages ; j++ ) {
2728                                 if ( stages[i].newStage->fragmentProgramImages[j] ) {
2729                                         stages[i].newStage->fragmentProgramImages[j]->Reload( false, force );
2730                                 }
2731                         }
2732                 } else if ( stages[i].texture.image ) {
2733                         stages[i].texture.image->Reload( false, force );
2734                 }
2735         }
2736 }