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