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