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