2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
36 Any errors during parsing just set MF_DEFAULTED and return, rather than throwing
37 a hard error. This will cause the material to fall back to default material,
38 but otherwise let things continue.
40 Each material may have a set of calculations that must be evaluated before
43 Every expression that a material uses can be evaluated at one time, which
44 will allow for perfect common subexpression removal when I get around to
47 Without this, scrolling an entire surface could result in evaluating the
48 same texture matrix calculations a half dozen times.
50 Open question: should I allow arbitrary per-vertex color, texCoord, and vertex
51 calculations to be specified in the material code?
53 Every stage will definately have a valid image pointer.
55 We might want the ability to change the sort value based on conditionals,
56 but it could be a hassle to implement,
60 // keep all of these on the stack, when they are static it makes material parsing non-reentrant
61 typedef struct mtrParsingData_s {
62 bool registerIsTemporary[MAX_EXPRESSION_REGISTERS];
63 float shaderRegisters[MAX_EXPRESSION_REGISTERS];
64 expOp_t shaderOps[MAX_EXPRESSION_OPS];
65 shaderStage_t parseStages[MAX_SHADER_STAGES];
67 bool registersAreConstant;
74 idMaterial::CommonInit
77 void idMaterial::CommonInit() {
80 contentFlags = CONTENTS_SOLID;
81 surfaceFlags = SURFTYPE_NONE;
85 cullType = CT_FRONT_SIDED;
90 expressionRegisters = NULL;
91 constantRegisters = NULL;
96 lightFalloffImage = NULL;
97 shouldCreateBackSides = false;
101 ambientLight = false;
104 allowOverlays = true;
105 unsmoothedTangents = false;
107 memset( deformRegisters, 0, sizeof( deformRegisters ) );
111 suppressInSubview = false;
115 decalInfo.stayTime = 10000;
116 decalInfo.fadeTime = 4000;
117 decalInfo.start[0] = 1;
118 decalInfo.start[1] = 1;
119 decalInfo.start[2] = 1;
120 decalInfo.start[3] = 1;
121 decalInfo.end[0] = 0;
122 decalInfo.end[1] = 0;
123 decalInfo.end[2] = 0;
124 decalInfo.end[3] = 0;
129 idMaterial::idMaterial
132 idMaterial::idMaterial() {
135 // we put this here instead of in CommonInit, because
136 // we don't want it cleared when a material is purged
142 idMaterial::~idMaterial
145 idMaterial::~idMaterial() {
153 void idMaterial::FreeData() {
157 // delete any idCinematic textures
158 for ( i = 0; i < numStages; i++ ) {
159 if ( stages[i].texture.cinematic != NULL ) {
160 delete stages[i].texture.cinematic;
161 stages[i].texture.cinematic = NULL;
163 if ( stages[i].newStage != NULL ) {
164 Mem_Free( stages[i].newStage );
165 stages[i].newStage = NULL;
168 R_StaticFree( stages );
171 if ( expressionRegisters != NULL ) {
172 R_StaticFree( expressionRegisters );
173 expressionRegisters = NULL;
175 if ( constantRegisters != NULL ) {
176 R_StaticFree( constantRegisters );
177 constantRegisters = NULL;
187 idMaterial::GetEditorImage
190 idImage *idMaterial::GetEditorImage( void ) const {
195 // if we don't have an editorImageName, use the first stage image
196 if ( !editorImageName.Length()) {
197 // _D3XP :: First check for a diffuse image, then use the first
198 if ( numStages && stages ) {
200 for( i = 0; i < numStages; i++ ) {
201 if ( stages[i].lighting == SL_DIFFUSE ) {
202 editorImage = stages[i].texture.image;
206 if ( !editorImage ) {
207 editorImage = stages[0].texture.image;
210 editorImage = globalImages->defaultImage;
213 // look for an explicit one
214 editorImage = globalImages->ImageFromFile( editorImageName, TF_DEFAULT, true, TR_REPEAT, TD_DEFAULT );
217 if ( !editorImage ) {
218 editorImage = globalImages->defaultImage;
228 int clearSolid, surfaceFlags, contents;
231 static infoParm_t infoParms[] = {
232 // game relevant attributes
233 {"solid", 0, 0, CONTENTS_SOLID }, // may need to override a clearSolid
234 {"water", 1, 0, CONTENTS_WATER }, // used for water
235 {"playerclip", 0, 0, CONTENTS_PLAYERCLIP }, // solid to players
236 {"monsterclip", 0, 0, CONTENTS_MONSTERCLIP }, // solid to monsters
237 {"moveableclip",0, 0, CONTENTS_MOVEABLECLIP },// solid to moveable entities
238 {"ikclip", 0, 0, CONTENTS_IKCLIP }, // solid to IK
239 {"blood", 0, 0, CONTENTS_BLOOD }, // used to detect blood decals
240 {"trigger", 0, 0, CONTENTS_TRIGGER }, // used for triggers
241 {"aassolid", 0, 0, CONTENTS_AAS_SOLID }, // solid for AAS
242 {"aasobstacle", 0, 0, CONTENTS_AAS_OBSTACLE },// used to compile an obstacle into AAS that can be enabled/disabled
243 {"flashlight_trigger", 0, 0, CONTENTS_FLASHLIGHT_TRIGGER }, // used for triggers that are activated by the flashlight
244 {"nonsolid", 1, 0, 0 }, // clears the solid flag
245 {"nullNormal", 0, SURF_NULLNORMAL,0 }, // renderbump will draw as 0x80 0x80 0x80
247 // utility relevant attributes
248 {"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas
249 {"qer_nocarve", 1, 0, CONTENTS_NOCSG}, // don't cut brushes in editor
251 {"discrete", 1, SURF_DISCRETE, 0 }, // surfaces should not be automatically merged together or
252 // clipped to the world,
253 // because they represent discrete objects like gui shaders
254 // mirrors, or autosprites
255 {"noFragment", 0, SURF_NOFRAGMENT, 0 },
257 {"slick", 0, SURF_SLICK, 0 },
258 {"collision", 0, SURF_COLLISION, 0 },
259 {"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks
260 {"nodamage", 0, SURF_NODAMAGE, 0 }, // no falling damage when hitting
261 {"ladder", 0, SURF_LADDER, 0 }, // climbable
262 {"nosteps", 0, SURF_NOSTEPS, 0 }, // no footsteps
264 // material types for particle, sound, footstep feedback
265 {"metal", 0, SURFTYPE_METAL, 0 }, // metal
266 {"stone", 0, SURFTYPE_STONE, 0 }, // stone
267 {"flesh", 0, SURFTYPE_FLESH, 0 }, // flesh
268 {"wood", 0, SURFTYPE_WOOD, 0 }, // wood
269 {"cardboard", 0, SURFTYPE_CARDBOARD, 0 }, // cardboard
270 {"liquid", 0, SURFTYPE_LIQUID, 0 }, // liquid
271 {"glass", 0, SURFTYPE_GLASS, 0 }, // glass
272 {"plastic", 0, SURFTYPE_PLASTIC, 0 }, // plastic
273 {"ricochet", 0, SURFTYPE_RICOCHET, 0 }, // behaves like metal but causes a ricochet sound
275 // unassigned surface types
276 {"surftype10", 0, SURFTYPE_10, 0 },
277 {"surftype11", 0, SURFTYPE_11, 0 },
278 {"surftype12", 0, SURFTYPE_12, 0 },
279 {"surftype13", 0, SURFTYPE_13, 0 },
280 {"surftype14", 0, SURFTYPE_14, 0 },
281 {"surftype15", 0, SURFTYPE_15, 0 },
284 static const int numInfoParms = sizeof(infoParms) / sizeof (infoParms[0]);
289 idMaterial::CheckSurfaceParm
291 See if the current token matches one of the surface parm bit flags
294 bool idMaterial::CheckSurfaceParm( idToken *token ) {
296 for ( int i = 0 ; i < numInfoParms ; i++ ) {
297 if ( !token->Icmp( infoParms[i].name ) ) {
298 if ( infoParms[i].surfaceFlags & SURF_TYPE_MASK ) {
299 // ensure we only have one surface type set
300 surfaceFlags &= ~SURF_TYPE_MASK;
302 surfaceFlags |= infoParms[i].surfaceFlags;
303 contentFlags |= infoParms[i].contents;
304 if ( infoParms[i].clearSolid ) {
305 contentFlags &= ~CONTENTS_SOLID;
315 idMaterial::MatchToken
317 Sets defaultShader and returns false if the next token doesn't match
320 bool idMaterial::MatchToken( idLexer &src, const char *match ) {
321 if ( !src.ExpectTokenString( match ) ) {
322 SetMaterialFlag( MF_DEFAULTED );
330 idMaterial::ParseSort
333 void idMaterial::ParseSort( idLexer &src ) {
336 if ( !src.ReadTokenOnLine( &token ) ) {
337 src.Warning( "missing sort parameter" );
338 SetMaterialFlag( MF_DEFAULTED );
342 if ( !token.Icmp( "subview" ) ) {
344 } else if ( !token.Icmp( "opaque" ) ) {
346 }else if ( !token.Icmp( "decal" ) ) {
348 } else if ( !token.Icmp( "far" ) ) {
350 } else if ( !token.Icmp( "medium" ) ) {
352 } else if ( !token.Icmp( "close" ) ) {
354 } else if ( !token.Icmp( "almostNearest" ) ) {
355 sort = SS_ALMOST_NEAREST;
356 } else if ( !token.Icmp( "nearest" ) ) {
358 } else if ( !token.Icmp( "postProcess" ) ) {
359 sort = SS_POST_PROCESS;
360 } else if ( !token.Icmp( "portalSky" ) ) {
361 sort = SS_PORTAL_SKY;
363 sort = atof( token );
369 idMaterial::ParseDecalInfo
372 void idMaterial::ParseDecalInfo( idLexer &src ) {
375 decalInfo.stayTime = src.ParseFloat() * 1000;
376 decalInfo.fadeTime = src.ParseFloat() * 1000;
377 float start[4], end[4];
378 src.Parse1DMatrix( 4, start );
379 src.Parse1DMatrix( 4, end );
380 for ( int i = 0 ; i < 4 ; i++ ) {
381 decalInfo.start[i] = start[i];
382 decalInfo.end[i] = end[i];
388 idMaterial::GetExpressionConstant
391 int idMaterial::GetExpressionConstant( float f ) {
394 for ( i = EXP_REG_NUM_PREDEFINED ; i < numRegisters ; i++ ) {
395 if ( !pd->registerIsTemporary[i] && pd->shaderRegisters[i] == f ) {
399 if ( numRegisters == MAX_EXPRESSION_REGISTERS ) {
400 common->Warning( "GetExpressionConstant: material '%s' hit MAX_EXPRESSION_REGISTERS", GetName() );
401 SetMaterialFlag( MF_DEFAULTED );
404 pd->registerIsTemporary[i] = false;
405 pd->shaderRegisters[i] = f;
413 idMaterial::GetExpressionTemporary
416 int idMaterial::GetExpressionTemporary( void ) {
417 if ( numRegisters == MAX_EXPRESSION_REGISTERS ) {
418 common->Warning( "GetExpressionTemporary: material '%s' hit MAX_EXPRESSION_REGISTERS", GetName() );
419 SetMaterialFlag( MF_DEFAULTED );
422 pd->registerIsTemporary[numRegisters] = true;
424 return numRegisters - 1;
429 idMaterial::GetExpressionOp
432 expOp_t *idMaterial::GetExpressionOp( void ) {
433 if ( numOps == MAX_EXPRESSION_OPS ) {
434 common->Warning( "GetExpressionOp: material '%s' hit MAX_EXPRESSION_OPS", GetName() );
435 SetMaterialFlag( MF_DEFAULTED );
436 return &pd->shaderOps[0];
439 return &pd->shaderOps[numOps++];
447 int idMaterial::EmitOp( int a, int b, expOpType_t opType ) {
450 // optimize away identity operations
451 if ( opType == OP_TYPE_ADD ) {
452 if ( !pd->registerIsTemporary[a] && pd->shaderRegisters[a] == 0 ) {
455 if ( !pd->registerIsTemporary[b] && pd->shaderRegisters[b] == 0 ) {
458 if ( !pd->registerIsTemporary[a] && !pd->registerIsTemporary[b] ) {
459 return GetExpressionConstant( pd->shaderRegisters[a] + pd->shaderRegisters[b] );
462 if ( opType == OP_TYPE_MULTIPLY ) {
463 if ( !pd->registerIsTemporary[a] && pd->shaderRegisters[a] == 1 ) {
466 if ( !pd->registerIsTemporary[a] && pd->shaderRegisters[a] == 0 ) {
469 if ( !pd->registerIsTemporary[b] && pd->shaderRegisters[b] == 1 ) {
472 if ( !pd->registerIsTemporary[b] && pd->shaderRegisters[b] == 0 ) {
475 if ( !pd->registerIsTemporary[a] && !pd->registerIsTemporary[b] ) {
476 return GetExpressionConstant( pd->shaderRegisters[a] * pd->shaderRegisters[b] );
480 op = GetExpressionOp();
484 op->c = GetExpressionTemporary();
491 idMaterial::ParseEmitOp
494 int idMaterial::ParseEmitOp( idLexer &src, int a, expOpType_t opType, int priority ) {
497 b = ParseExpressionPriority( src, priority );
498 return EmitOp( a, b, opType );
503 idMaterial::ParseTerm
505 Returns a register index
508 int idMaterial::ParseTerm( idLexer &src ) {
512 src.ReadToken( &token );
514 if ( token == "(" ) {
515 a = ParseExpression( src );
516 MatchToken( src, ")" );
520 if ( !token.Icmp( "time" ) ) {
521 pd->registersAreConstant = false;
524 if ( !token.Icmp( "parm0" ) ) {
525 pd->registersAreConstant = false;
526 return EXP_REG_PARM0;
528 if ( !token.Icmp( "parm1" ) ) {
529 pd->registersAreConstant = false;
530 return EXP_REG_PARM1;
532 if ( !token.Icmp( "parm2" ) ) {
533 pd->registersAreConstant = false;
534 return EXP_REG_PARM2;
536 if ( !token.Icmp( "parm3" ) ) {
537 pd->registersAreConstant = false;
538 return EXP_REG_PARM3;
540 if ( !token.Icmp( "parm4" ) ) {
541 pd->registersAreConstant = false;
542 return EXP_REG_PARM4;
544 if ( !token.Icmp( "parm5" ) ) {
545 pd->registersAreConstant = false;
546 return EXP_REG_PARM5;
548 if ( !token.Icmp( "parm6" ) ) {
549 pd->registersAreConstant = false;
550 return EXP_REG_PARM6;
552 if ( !token.Icmp( "parm7" ) ) {
553 pd->registersAreConstant = false;
554 return EXP_REG_PARM7;
556 if ( !token.Icmp( "parm8" ) ) {
557 pd->registersAreConstant = false;
558 return EXP_REG_PARM8;
560 if ( !token.Icmp( "parm9" ) ) {
561 pd->registersAreConstant = false;
562 return EXP_REG_PARM9;
564 if ( !token.Icmp( "parm10" ) ) {
565 pd->registersAreConstant = false;
566 return EXP_REG_PARM10;
568 if ( !token.Icmp( "parm11" ) ) {
569 pd->registersAreConstant = false;
570 return EXP_REG_PARM11;
572 if ( !token.Icmp( "global0" ) ) {
573 pd->registersAreConstant = false;
574 return EXP_REG_GLOBAL0;
576 if ( !token.Icmp( "global1" ) ) {
577 pd->registersAreConstant = false;
578 return EXP_REG_GLOBAL1;
580 if ( !token.Icmp( "global2" ) ) {
581 pd->registersAreConstant = false;
582 return EXP_REG_GLOBAL2;
584 if ( !token.Icmp( "global3" ) ) {
585 pd->registersAreConstant = false;
586 return EXP_REG_GLOBAL3;
588 if ( !token.Icmp( "global4" ) ) {
589 pd->registersAreConstant = false;
590 return EXP_REG_GLOBAL4;
592 if ( !token.Icmp( "global5" ) ) {
593 pd->registersAreConstant = false;
594 return EXP_REG_GLOBAL5;
596 if ( !token.Icmp( "global6" ) ) {
597 pd->registersAreConstant = false;
598 return EXP_REG_GLOBAL6;
600 if ( !token.Icmp( "global7" ) ) {
601 pd->registersAreConstant = false;
602 return EXP_REG_GLOBAL7;
604 if ( !token.Icmp( "fragmentPrograms" ) ) {
605 return GetExpressionConstant( (float) glConfig.ARBFragmentProgramAvailable );
608 if ( !token.Icmp( "sound" ) ) {
609 pd->registersAreConstant = false;
610 return EmitOp( 0, 0, OP_TYPE_SOUND );
613 // parse negative numbers
614 if ( token == "-" ) {
615 src.ReadToken( &token );
616 if ( token.type == TT_NUMBER || token == "." ) {
617 return GetExpressionConstant( -(float) token.GetFloatValue() );
619 src.Warning( "Bad negative number '%s'", token.c_str() );
620 SetMaterialFlag( MF_DEFAULTED );
624 if ( token.type == TT_NUMBER || token == "." || token == "-" ) {
625 return GetExpressionConstant( (float) token.GetFloatValue() );
628 // see if it is a table name
629 const idDeclTable *table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, token.c_str(), false ) );
631 src.Warning( "Bad term '%s'", token.c_str() );
632 SetMaterialFlag( MF_DEFAULTED );
636 // parse a table expression
637 MatchToken( src, "[" );
639 b = ParseExpression( src );
641 MatchToken( src, "]" );
643 return EmitOp( table->Index(), b, OP_TYPE_TABLE );
648 idMaterial::ParseExpressionPriority
650 Returns a register index
653 #define TOP_PRIORITY 4
654 int idMaterial::ParseExpressionPriority( idLexer &src, int priority ) {
658 if ( priority == 0 ) {
659 return ParseTerm( src );
662 a = ParseExpressionPriority( src, priority - 1 );
664 if ( TestMaterialFlag( MF_DEFAULTED ) ) { // we have a parse error
668 if ( !src.ReadToken( &token ) ) {
669 // we won't get EOF in a real file, but we can
670 // when parsing from generated strings
674 if ( priority == 1 && token == "*" ) {
675 return ParseEmitOp( src, a, OP_TYPE_MULTIPLY, priority );
677 if ( priority == 1 && token == "/" ) {
678 return ParseEmitOp( src, a, OP_TYPE_DIVIDE, priority );
680 if ( priority == 1 && token == "%" ) { // implied truncate both to integer
681 return ParseEmitOp( src, a, OP_TYPE_MOD, priority );
683 if ( priority == 2 && token == "+" ) {
684 return ParseEmitOp( src, a, OP_TYPE_ADD, priority );
686 if ( priority == 2 && token == "-" ) {
687 return ParseEmitOp( src, a, OP_TYPE_SUBTRACT, priority );
689 if ( priority == 3 && token == ">" ) {
690 return ParseEmitOp( src, a, OP_TYPE_GT, priority );
692 if ( priority == 3 && token == ">=" ) {
693 return ParseEmitOp( src, a, OP_TYPE_GE, priority );
695 if ( priority == 3 && token == "<" ) {
696 return ParseEmitOp( src, a, OP_TYPE_LT, priority );
698 if ( priority == 3 && token == "<=" ) {
699 return ParseEmitOp( src, a, OP_TYPE_LE, priority );
701 if ( priority == 3 && token == "==" ) {
702 return ParseEmitOp( src, a, OP_TYPE_EQ, priority );
704 if ( priority == 3 && token == "!=" ) {
705 return ParseEmitOp( src, a, OP_TYPE_NE, priority );
707 if ( priority == 4 && token == "&&" ) {
708 return ParseEmitOp( src, a, OP_TYPE_AND, priority );
710 if ( priority == 4 && token == "||" ) {
711 return ParseEmitOp( src, a, OP_TYPE_OR, priority );
714 // assume that anything else terminates the expression
715 // not too robust error checking...
717 src.UnreadToken( &token );
724 idMaterial::ParseExpression
726 Returns a register index
729 int idMaterial::ParseExpression( idLexer &src ) {
730 return ParseExpressionPriority( src, TOP_PRIORITY );
736 idMaterial::ClearStage
739 void idMaterial::ClearStage( shaderStage_t *ss ) {
740 ss->drawStateBits = 0;
741 ss->conditionRegister = GetExpressionConstant( 1 );
742 ss->color.registers[0] =
743 ss->color.registers[1] =
744 ss->color.registers[2] =
745 ss->color.registers[3] = GetExpressionConstant( 1 );
750 idMaterial::NameToSrcBlendMode
753 int idMaterial::NameToSrcBlendMode( const idStr &name ) {
754 if ( !name.Icmp( "GL_ONE" ) ) {
755 return GLS_SRCBLEND_ONE;
756 } else if ( !name.Icmp( "GL_ZERO" ) ) {
757 return GLS_SRCBLEND_ZERO;
758 } else if ( !name.Icmp( "GL_DST_COLOR" ) ) {
759 return GLS_SRCBLEND_DST_COLOR;
760 } else if ( !name.Icmp( "GL_ONE_MINUS_DST_COLOR" ) ) {
761 return GLS_SRCBLEND_ONE_MINUS_DST_COLOR;
762 } else if ( !name.Icmp( "GL_SRC_ALPHA" ) ) {
763 return GLS_SRCBLEND_SRC_ALPHA;
764 } else if ( !name.Icmp( "GL_ONE_MINUS_SRC_ALPHA" ) ) {
765 return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA;
766 } else if ( !name.Icmp( "GL_DST_ALPHA" ) ) {
767 return GLS_SRCBLEND_DST_ALPHA;
768 } else if ( !name.Icmp( "GL_ONE_MINUS_DST_ALPHA" ) ) {
769 return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA;
770 } else if ( !name.Icmp( "GL_SRC_ALPHA_SATURATE" ) ) {
771 return GLS_SRCBLEND_ALPHA_SATURATE;
774 common->Warning( "unknown blend mode '%s' in material '%s'", name.c_str(), GetName() );
775 SetMaterialFlag( MF_DEFAULTED );
777 return GLS_SRCBLEND_ONE;
782 idMaterial::NameToDstBlendMode
785 int idMaterial::NameToDstBlendMode( const idStr &name ) {
786 if ( !name.Icmp( "GL_ONE" ) ) {
787 return GLS_DSTBLEND_ONE;
788 } else if ( !name.Icmp( "GL_ZERO" ) ) {
789 return GLS_DSTBLEND_ZERO;
790 } else if ( !name.Icmp( "GL_SRC_ALPHA" ) ) {
791 return GLS_DSTBLEND_SRC_ALPHA;
792 } else if ( !name.Icmp( "GL_ONE_MINUS_SRC_ALPHA" ) ) {
793 return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
794 } else if ( !name.Icmp( "GL_DST_ALPHA" ) ) {
795 return GLS_DSTBLEND_DST_ALPHA;
796 } else if ( !name.Icmp( "GL_ONE_MINUS_DST_ALPHA" ) ) {
797 return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA;
798 } else if ( !name.Icmp( "GL_SRC_COLOR" ) ) {
799 return GLS_DSTBLEND_SRC_COLOR;
800 } else if ( !name.Icmp( "GL_ONE_MINUS_SRC_COLOR" ) ) {
801 return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR;
804 common->Warning( "unknown blend mode '%s' in material '%s'", name.c_str(), GetName() );
805 SetMaterialFlag( MF_DEFAULTED );
807 return GLS_DSTBLEND_ONE;
812 idMaterial::ParseBlend
815 void idMaterial::ParseBlend( idLexer &src, shaderStage_t *stage ) {
817 int srcBlend, dstBlend;
819 if ( !src.ReadToken( &token ) ) {
823 // blending combinations
824 if ( !token.Icmp( "blend" ) ) {
825 stage->drawStateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
828 if ( !token.Icmp( "add" ) ) {
829 stage->drawStateBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE;
832 if ( !token.Icmp( "filter" ) || !token.Icmp( "modulate" ) ) {
833 stage->drawStateBits = GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO;
836 if ( !token.Icmp( "none" ) ) {
837 // none is used when defining an alpha mask that doesn't draw
838 stage->drawStateBits = GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE;
841 if ( !token.Icmp( "bumpmap" ) ) {
842 stage->lighting = SL_BUMP;
845 if ( !token.Icmp( "diffusemap" ) ) {
846 stage->lighting = SL_DIFFUSE;
849 if ( !token.Icmp( "specularmap" ) ) {
850 stage->lighting = SL_SPECULAR;
854 srcBlend = NameToSrcBlendMode( token );
856 MatchToken( src, "," );
857 if ( !src.ReadToken( &token ) ) {
860 dstBlend = NameToDstBlendMode( token );
862 stage->drawStateBits = srcBlend | dstBlend;
867 idMaterial::ParseVertexParm
869 If there is a single value, it will be repeated across all elements
870 If there are two values, 3 = 0.0, 4 = 1.0
871 if there are three values, 4 = 1.0
874 void idMaterial::ParseVertexParm( idLexer &src, newShaderStage_t *newStage ) {
877 src.ReadTokenOnLine( &token );
878 int parm = token.GetIntValue();
879 if ( !token.IsNumeric() || parm < 0 || parm >= MAX_VERTEX_PARMS ) {
880 common->Warning( "bad vertexParm number\n" );
881 SetMaterialFlag( MF_DEFAULTED );
884 if ( parm >= newStage->numVertexParms ) {
885 newStage->numVertexParms = parm+1;
888 newStage->vertexParms[parm][0] = ParseExpression( src );
890 src.ReadTokenOnLine( &token );
891 if ( !token[0] || token.Icmp( "," ) ) {
892 newStage->vertexParms[parm][1] =
893 newStage->vertexParms[parm][2] =
894 newStage->vertexParms[parm][3] = newStage->vertexParms[parm][0];
898 newStage->vertexParms[parm][1] = ParseExpression( src );
900 src.ReadTokenOnLine( &token );
901 if ( !token[0] || token.Icmp( "," ) ) {
902 newStage->vertexParms[parm][2] = GetExpressionConstant( 0 );
903 newStage->vertexParms[parm][3] = GetExpressionConstant( 1 );
907 newStage->vertexParms[parm][2] = ParseExpression( src );
909 src.ReadTokenOnLine( &token );
910 if ( !token[0] || token.Icmp( "," ) ) {
911 newStage->vertexParms[parm][3] = GetExpressionConstant( 1 );
915 newStage->vertexParms[parm][3] = ParseExpression( src );
921 idMaterial::ParseFragmentMap
924 void idMaterial::ParseFragmentMap( idLexer &src, newShaderStage_t *newStage ) {
939 src.ReadTokenOnLine( &token );
940 int unit = token.GetIntValue();
941 if ( !token.IsNumeric() || unit < 0 || unit >= MAX_FRAGMENT_IMAGES ) {
942 common->Warning( "bad fragmentMap number\n" );
943 SetMaterialFlag( MF_DEFAULTED );
947 // unit 1 is the normal map.. make sure it gets flagged as the proper depth
952 if ( unit >= newStage->numFragmentProgramImages ) {
953 newStage->numFragmentProgramImages = unit+1;
957 src.ReadTokenOnLine( &token );
959 if ( !token.Icmp( "cubeMap" ) ) {
963 if ( !token.Icmp( "cameraCubeMap" ) ) {
967 if ( !token.Icmp( "nearest" ) ) {
971 if ( !token.Icmp( "linear" ) ) {
975 if ( !token.Icmp( "clamp" ) ) {
979 if ( !token.Icmp( "noclamp" ) ) {
983 if ( !token.Icmp( "zeroclamp" ) ) {
984 trp = TR_CLAMP_TO_ZERO;
987 if ( !token.Icmp( "alphazeroclamp" ) ) {
988 trp = TR_CLAMP_TO_ZERO_ALPHA;
991 if ( !token.Icmp( "forceHighQuality" ) ) {
992 td = TD_HIGH_QUALITY;
996 if ( !token.Icmp( "uncompressed" ) || !token.Icmp( "highquality" ) ) {
997 if ( !globalImages->image_ignoreHighQuality.GetInteger() ) {
998 td = TD_HIGH_QUALITY;
1002 if ( !token.Icmp( "nopicmip" ) ) {
1003 allowPicmip = false;
1007 // assume anything else is the image name
1008 src.UnreadToken( &token );
1011 str = R_ParsePastImageProgram( src );
1013 newStage->fragmentProgramImages[unit] =
1014 globalImages->ImageFromFile( str, tf, allowPicmip, trp, td, cubeMap );
1015 if ( !newStage->fragmentProgramImages[unit] ) {
1016 newStage->fragmentProgramImages[unit] = globalImages->defaultImage;
1022 idMaterial::MultiplyTextureMatrix
1025 void idMaterial::MultiplyTextureMatrix( textureStage_t *ts, int registers[2][3] ) {
1028 if ( !ts->hasMatrix ) {
1029 ts->hasMatrix = true;
1030 memcpy( ts->matrix, registers, sizeof( ts->matrix ) );
1034 memcpy( old, ts->matrix, sizeof( old ) );
1036 // multiply the two maticies
1037 ts->matrix[0][0] = EmitOp(
1038 EmitOp( old[0][0], registers[0][0], OP_TYPE_MULTIPLY ),
1039 EmitOp( old[0][1], registers[1][0], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1040 ts->matrix[0][1] = EmitOp(
1041 EmitOp( old[0][0], registers[0][1], OP_TYPE_MULTIPLY ),
1042 EmitOp( old[0][1], registers[1][1], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1043 ts->matrix[0][2] = EmitOp(
1045 EmitOp( old[0][0], registers[0][2], OP_TYPE_MULTIPLY ),
1046 EmitOp( old[0][1], registers[1][2], OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1047 old[0][2], OP_TYPE_ADD );
1049 ts->matrix[1][0] = EmitOp(
1050 EmitOp( old[1][0], registers[0][0], OP_TYPE_MULTIPLY ),
1051 EmitOp( old[1][1], registers[1][0], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1052 ts->matrix[1][1] = EmitOp(
1053 EmitOp( old[1][0], registers[0][1], OP_TYPE_MULTIPLY ),
1054 EmitOp( old[1][1], registers[1][1], OP_TYPE_MULTIPLY ), OP_TYPE_ADD );
1055 ts->matrix[1][2] = EmitOp(
1057 EmitOp( old[1][0], registers[0][2], OP_TYPE_MULTIPLY ),
1058 EmitOp( old[1][1], registers[1][2], OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1059 old[1][2], OP_TYPE_ADD );
1065 idMaterial::ParseStage
1067 An open brace has been parsed
1073 "nearest" "linear" "clamp" "zeroclamp" "uncompressed" "highquality" "nopicmip"
1074 scroll, scale, rotate
1079 void idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
1085 textureRepeat_t trp;
1087 cubeFiles_t cubeMap;
1089 char imageName[MAX_IMAGE_NAME];
1092 newShaderStage_t newStage;
1094 if ( numStages >= MAX_SHADER_STAGES ) {
1095 SetMaterialFlag( MF_DEFAULTED );
1096 common->Warning( "material '%s' exceeded %i stages", GetName(), MAX_SHADER_STAGES );
1107 memset( &newStage, 0, sizeof( newStage ) );
1109 ss = &pd->parseStages[numStages];
1115 if ( TestMaterialFlag( MF_DEFAULTED ) ) { // we have a parse error
1118 if ( !src.ExpectAnyToken( &token ) ) {
1119 SetMaterialFlag( MF_DEFAULTED );
1123 // the close brace for the entire material ends the draw block
1124 if ( token == "}" ) {
1128 //BSM Nerve: Added for stage naming in the material editor
1129 if( !token.Icmp( "name") ) {
1130 src.SkipRestOfLine();
1135 if ( !token.Icmp( "blend" ) ) {
1136 ParseBlend( src, ss );
1140 if ( !token.Icmp( "map" ) ) {
1141 str = R_ParsePastImageProgram( src );
1142 idStr::Copynz( imageName, str, sizeof( imageName ) );
1146 if ( !token.Icmp( "remoteRenderMap" ) ) {
1147 ts->dynamic = DI_REMOTE_RENDER;
1148 ts->width = src.ParseInt();
1149 ts->height = src.ParseInt();
1153 if ( !token.Icmp( "mirrorRenderMap" ) ) {
1154 ts->dynamic = DI_MIRROR_RENDER;
1155 ts->width = src.ParseInt();
1156 ts->height = src.ParseInt();
1157 ts->texgen = TG_SCREEN;
1161 if ( !token.Icmp( "xrayRenderMap" ) ) {
1162 ts->dynamic = DI_XRAY_RENDER;
1163 ts->width = src.ParseInt();
1164 ts->height = src.ParseInt();
1165 ts->texgen = TG_SCREEN;
1168 if ( !token.Icmp( "screen" ) ) {
1169 ts->texgen = TG_SCREEN;
1172 if ( !token.Icmp( "screen2" ) ) {
1173 ts->texgen = TG_SCREEN2;
1176 if ( !token.Icmp( "glassWarp" ) ) {
1177 ts->texgen = TG_GLASSWARP;
1181 if ( !token.Icmp( "videomap" ) ) {
1182 // note that videomaps will always be in clamp mode, so texture
1183 // coordinates had better be in the 0 to 1 range
1184 if ( !src.ReadToken( &token ) ) {
1185 common->Warning( "missing parameter for 'videoMap' keyword in material '%s'", GetName() );
1189 if ( !token.Icmp( "loop" ) ) {
1191 if ( !src.ReadToken( &token ) ) {
1192 common->Warning( "missing parameter for 'videoMap' keyword in material '%s'", GetName() );
1196 ts->cinematic = idCinematic::Alloc();
1197 ts->cinematic->InitFromFile( token.c_str(), loop );
1201 if ( !token.Icmp( "soundmap" ) ) {
1202 if ( !src.ReadToken( &token ) ) {
1203 common->Warning( "missing parameter for 'soundmap' keyword in material '%s'", GetName() );
1206 ts->cinematic = new idSndWindow();
1207 ts->cinematic->InitFromFile( token.c_str(), true );
1211 if ( !token.Icmp( "cubeMap" ) ) {
1212 str = R_ParsePastImageProgram( src );
1213 idStr::Copynz( imageName, str, sizeof( imageName ) );
1214 cubeMap = CF_NATIVE;
1218 if ( !token.Icmp( "cameraCubeMap" ) ) {
1219 str = R_ParsePastImageProgram( src );
1220 idStr::Copynz( imageName, str, sizeof( imageName ) );
1221 cubeMap = CF_CAMERA;
1225 if ( !token.Icmp( "ignoreAlphaTest" ) ) {
1226 ss->ignoreAlphaTest = true;
1229 if ( !token.Icmp( "nearest" ) ) {
1233 if ( !token.Icmp( "linear" ) ) {
1237 if ( !token.Icmp( "clamp" ) ) {
1241 if ( !token.Icmp( "noclamp" ) ) {
1245 if ( !token.Icmp( "zeroclamp" ) ) {
1246 trp = TR_CLAMP_TO_ZERO;
1249 if ( !token.Icmp( "alphazeroclamp" ) ) {
1250 trp = TR_CLAMP_TO_ZERO_ALPHA;
1253 if ( !token.Icmp( "uncompressed" ) || !token.Icmp( "highquality" ) ) {
1254 if ( !globalImages->image_ignoreHighQuality.GetInteger() ) {
1255 td = TD_HIGH_QUALITY;
1259 if ( !token.Icmp( "forceHighQuality" ) ) {
1260 td = TD_HIGH_QUALITY;
1263 if ( !token.Icmp( "nopicmip" ) ) {
1264 allowPicmip = false;
1267 if ( !token.Icmp( "vertexColor" ) ) {
1268 ss->vertexColor = SVC_MODULATE;
1271 if ( !token.Icmp( "inverseVertexColor" ) ) {
1272 ss->vertexColor = SVC_INVERSE_MODULATE;
1276 // privatePolygonOffset
1277 else if ( !token.Icmp( "privatePolygonOffset" ) ) {
1278 if ( !src.ReadTokenOnLine( &token ) ) {
1279 ss->privatePolygonOffset = 1;
1282 // explict larger (or negative) offset
1283 src.UnreadToken( &token );
1284 ss->privatePolygonOffset = src.ParseFloat();
1288 // texture coordinate generation
1289 if ( !token.Icmp( "texGen" ) ) {
1290 src.ExpectAnyToken( &token );
1291 if ( !token.Icmp( "normal" ) ) {
1292 ts->texgen = TG_DIFFUSE_CUBE;
1293 } else if ( !token.Icmp( "reflect" ) ) {
1294 ts->texgen = TG_REFLECT_CUBE;
1295 } else if ( !token.Icmp( "skybox" ) ) {
1296 ts->texgen = TG_SKYBOX_CUBE;
1297 } else if ( !token.Icmp( "wobbleSky" ) ) {
1298 ts->texgen = TG_WOBBLESKY_CUBE;
1299 texGenRegisters[0] = ParseExpression( src );
1300 texGenRegisters[1] = ParseExpression( src );
1301 texGenRegisters[2] = ParseExpression( src );
1303 common->Warning( "bad texGen '%s' in material %s", token.c_str(), GetName() );
1304 SetMaterialFlag( MF_DEFAULTED );
1308 if ( !token.Icmp( "scroll" ) || !token.Icmp( "translate" ) ) {
1309 a = ParseExpression( src );
1310 MatchToken( src, "," );
1311 b = ParseExpression( src );
1312 matrix[0][0] = GetExpressionConstant( 1 );
1313 matrix[0][1] = GetExpressionConstant( 0 );
1315 matrix[1][0] = GetExpressionConstant( 0 );
1316 matrix[1][1] = GetExpressionConstant( 1 );
1319 MultiplyTextureMatrix( ts, matrix );
1322 if ( !token.Icmp( "scale" ) ) {
1323 a = ParseExpression( src );
1324 MatchToken( src, "," );
1325 b = ParseExpression( src );
1326 // this just scales without a centering
1328 matrix[0][1] = GetExpressionConstant( 0 );
1329 matrix[0][2] = GetExpressionConstant( 0 );
1330 matrix[1][0] = GetExpressionConstant( 0 );
1332 matrix[1][2] = GetExpressionConstant( 0 );
1334 MultiplyTextureMatrix( ts, matrix );
1337 if ( !token.Icmp( "centerScale" ) ) {
1338 a = ParseExpression( src );
1339 MatchToken( src, "," );
1340 b = ParseExpression( src );
1341 // this subtracts 0.5, then scales, then adds 0.5
1343 matrix[0][1] = GetExpressionConstant( 0 );
1344 matrix[0][2] = EmitOp( GetExpressionConstant( 0.5 ), EmitOp( GetExpressionConstant( 0.5 ), a, OP_TYPE_MULTIPLY ), OP_TYPE_SUBTRACT );
1345 matrix[1][0] = GetExpressionConstant( 0 );
1347 matrix[1][2] = EmitOp( GetExpressionConstant( 0.5 ), EmitOp( GetExpressionConstant( 0.5 ), b, OP_TYPE_MULTIPLY ), OP_TYPE_SUBTRACT );
1349 MultiplyTextureMatrix( ts, matrix );
1352 if ( !token.Icmp( "shear" ) ) {
1353 a = ParseExpression( src );
1354 MatchToken( src, "," );
1355 b = ParseExpression( src );
1356 // this subtracts 0.5, then shears, then adds 0.5
1357 matrix[0][0] = GetExpressionConstant( 1 );
1359 matrix[0][2] = EmitOp( GetExpressionConstant( -0.5 ), a, OP_TYPE_MULTIPLY );
1361 matrix[1][1] = GetExpressionConstant( 1 );
1362 matrix[1][2] = EmitOp( GetExpressionConstant( -0.5 ), b, OP_TYPE_MULTIPLY );
1364 MultiplyTextureMatrix( ts, matrix );
1367 if ( !token.Icmp( "rotate" ) ) {
1368 const idDeclTable *table;
1372 a = ParseExpression( src );
1374 table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, "sinTable", false ) );
1376 common->Warning( "no sinTable for rotate defined" );
1377 SetMaterialFlag( MF_DEFAULTED );
1380 sinReg = EmitOp( table->Index(), a, OP_TYPE_TABLE );
1382 table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, "cosTable", false ) );
1384 common->Warning( "no cosTable for rotate defined" );
1385 SetMaterialFlag( MF_DEFAULTED );
1388 cosReg = EmitOp( table->Index(), a, OP_TYPE_TABLE );
1390 // this subtracts 0.5, then rotates, then adds 0.5
1391 matrix[0][0] = cosReg;
1392 matrix[0][1] = EmitOp( GetExpressionConstant( 0 ), sinReg, OP_TYPE_SUBTRACT );
1393 matrix[0][2] = EmitOp( EmitOp( EmitOp( GetExpressionConstant( -0.5 ), cosReg, OP_TYPE_MULTIPLY ),
1394 EmitOp( GetExpressionConstant( 0.5 ), sinReg, OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1395 GetExpressionConstant( 0.5 ), OP_TYPE_ADD );
1397 matrix[1][0] = sinReg;
1398 matrix[1][1] = cosReg;
1399 matrix[1][2] = EmitOp( EmitOp( EmitOp( GetExpressionConstant( -0.5 ), sinReg, OP_TYPE_MULTIPLY ),
1400 EmitOp( GetExpressionConstant( -0.5 ), cosReg, OP_TYPE_MULTIPLY ), OP_TYPE_ADD ),
1401 GetExpressionConstant( 0.5 ), OP_TYPE_ADD );
1403 MultiplyTextureMatrix( ts, matrix );
1407 // color mask options
1408 if ( !token.Icmp( "maskRed" ) ) {
1409 ss->drawStateBits |= GLS_REDMASK;
1412 if ( !token.Icmp( "maskGreen" ) ) {
1413 ss->drawStateBits |= GLS_GREENMASK;
1416 if ( !token.Icmp( "maskBlue" ) ) {
1417 ss->drawStateBits |= GLS_BLUEMASK;
1420 if ( !token.Icmp( "maskAlpha" ) ) {
1421 ss->drawStateBits |= GLS_ALPHAMASK;
1424 if ( !token.Icmp( "maskColor" ) ) {
1425 ss->drawStateBits |= GLS_COLORMASK;
1428 if ( !token.Icmp( "maskDepth" ) ) {
1429 ss->drawStateBits |= GLS_DEPTHMASK;
1432 if ( !token.Icmp( "alphaTest" ) ) {
1433 ss->hasAlphaTest = true;
1434 ss->alphaTestRegister = ParseExpression( src );
1435 coverage = MC_PERFORATED;
1439 // shorthand for 2D modulated
1440 if ( !token.Icmp( "colored" ) ) {
1441 ss->color.registers[0] = EXP_REG_PARM0;
1442 ss->color.registers[1] = EXP_REG_PARM1;
1443 ss->color.registers[2] = EXP_REG_PARM2;
1444 ss->color.registers[3] = EXP_REG_PARM3;
1445 pd->registersAreConstant = false;
1449 if ( !token.Icmp( "color" ) ) {
1450 ss->color.registers[0] = ParseExpression( src );
1451 MatchToken( src, "," );
1452 ss->color.registers[1] = ParseExpression( src );
1453 MatchToken( src, "," );
1454 ss->color.registers[2] = ParseExpression( src );
1455 MatchToken( src, "," );
1456 ss->color.registers[3] = ParseExpression( src );
1459 if ( !token.Icmp( "red" ) ) {
1460 ss->color.registers[0] = ParseExpression( src );
1463 if ( !token.Icmp( "green" ) ) {
1464 ss->color.registers[1] = ParseExpression( src );
1467 if ( !token.Icmp( "blue" ) ) {
1468 ss->color.registers[2] = ParseExpression( src );
1471 if ( !token.Icmp( "alpha" ) ) {
1472 ss->color.registers[3] = ParseExpression( src );
1475 if ( !token.Icmp( "rgb" ) ) {
1476 ss->color.registers[0] = ss->color.registers[1] =
1477 ss->color.registers[2] = ParseExpression( src );
1480 if ( !token.Icmp( "rgba" ) ) {
1481 ss->color.registers[0] = ss->color.registers[1] =
1482 ss->color.registers[2] = ss->color.registers[3] = ParseExpression( src );
1486 if ( !token.Icmp( "if" ) ) {
1487 ss->conditionRegister = ParseExpression( src );
1490 if ( !token.Icmp( "program" ) ) {
1491 if ( src.ReadTokenOnLine( &token ) ) {
1492 newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() );
1493 newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() );
1497 if ( !token.Icmp( "fragmentProgram" ) ) {
1498 if ( src.ReadTokenOnLine( &token ) ) {
1499 newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() );
1503 if ( !token.Icmp( "vertexProgram" ) ) {
1504 if ( src.ReadTokenOnLine( &token ) ) {
1505 newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() );
1509 if ( !token.Icmp( "megaTexture" ) ) {
1510 if ( src.ReadTokenOnLine( &token ) ) {
1511 newStage.megaTexture = new idMegaTexture;
1512 if ( !newStage.megaTexture->InitFromMegaFile( token.c_str() ) ) {
1513 delete newStage.megaTexture;
1514 SetMaterialFlag( MF_DEFAULTED );
1517 newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "megaTexture.vfp" );
1518 newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "megaTexture.vfp" );
1524 if ( !token.Icmp( "vertexParm" ) ) {
1525 ParseVertexParm( src, &newStage );
1529 if ( !token.Icmp( "fragmentMap" ) ) {
1530 ParseFragmentMap( src, &newStage );
1535 common->Warning( "unknown token '%s' in material '%s'", token.c_str(), GetName() );
1536 SetMaterialFlag( MF_DEFAULTED );
1541 // if we are using newStage, allocate a copy of it
1542 if ( newStage.fragmentProgram || newStage.vertexProgram ) {
1543 ss->newStage = (newShaderStage_t *)Mem_Alloc( sizeof( newStage ) );
1544 *(ss->newStage) = newStage;
1547 // successfully parsed a stage
1550 // select a compressed depth based on what the stage is
1551 if ( td == TD_DEFAULT ) {
1552 switch( ss->lighting ) {
1567 // now load the image with all the parms we parsed
1568 if ( imageName[0] ) {
1569 ts->image = globalImages->ImageFromFile( imageName, tf, allowPicmip, trp, td, cubeMap );
1571 ts->image = globalImages->defaultImage;
1573 } else if ( !ts->cinematic && !ts->dynamic && !ss->newStage ) {
1574 common->Warning( "material '%s' had stage with no image", GetName() );
1575 ts->image = globalImages->defaultImage;
1581 idMaterial::ParseDeform
1584 void idMaterial::ParseDeform( idLexer &src ) {
1587 if ( !src.ExpectAnyToken( &token ) ) {
1591 if ( !token.Icmp( "sprite" ) ) {
1592 deform = DFRM_SPRITE;
1593 cullType = CT_TWO_SIDED;
1594 SetMaterialFlag( MF_NOSHADOWS );
1597 if ( !token.Icmp( "tube" ) ) {
1599 cullType = CT_TWO_SIDED;
1600 SetMaterialFlag( MF_NOSHADOWS );
1603 if ( !token.Icmp( "flare" ) ) {
1604 deform = DFRM_FLARE;
1605 cullType = CT_TWO_SIDED;
1606 deformRegisters[0] = ParseExpression( src );
1607 SetMaterialFlag( MF_NOSHADOWS );
1610 if ( !token.Icmp( "expand" ) ) {
1611 deform = DFRM_EXPAND;
1612 deformRegisters[0] = ParseExpression( src );
1615 if ( !token.Icmp( "move" ) ) {
1617 deformRegisters[0] = ParseExpression( src );
1620 if ( !token.Icmp( "turbulent" ) ) {
1623 if ( !src.ExpectAnyToken( &token ) ) {
1624 src.Warning( "deform particle missing particle name" );
1625 SetMaterialFlag( MF_DEFAULTED );
1628 deformDecl = declManager->FindType( DECL_TABLE, token.c_str(), true );
1630 deformRegisters[0] = ParseExpression( src );
1631 deformRegisters[1] = ParseExpression( src );
1632 deformRegisters[2] = ParseExpression( src );
1635 if ( !token.Icmp( "eyeBall" ) ) {
1636 deform = DFRM_EYEBALL;
1639 if ( !token.Icmp( "particle" ) ) {
1640 deform = DFRM_PARTICLE;
1641 if ( !src.ExpectAnyToken( &token ) ) {
1642 src.Warning( "deform particle missing particle name" );
1643 SetMaterialFlag( MF_DEFAULTED );
1646 deformDecl = declManager->FindType( DECL_PARTICLE, token.c_str(), true );
1649 if ( !token.Icmp( "particle2" ) ) {
1650 deform = DFRM_PARTICLE2;
1651 if ( !src.ExpectAnyToken( &token ) ) {
1652 src.Warning( "deform particle missing particle name" );
1653 SetMaterialFlag( MF_DEFAULTED );
1656 deformDecl = declManager->FindType( DECL_PARTICLE, token.c_str(), true );
1659 src.Warning( "Bad deform type '%s'", token.c_str() );
1660 SetMaterialFlag( MF_DEFAULTED );
1666 idMaterial::AddImplicitStages
1668 If a material has diffuse or specular stages without any
1669 bump stage, add an implicit _flat bumpmap stage.
1671 If a material has a bump stage but no diffuse or specular
1672 stage, add a _white diffuse stage.
1674 It is valid to have either a diffuse or specular without the other.
1676 It is valid to have a reflection map and a bump map for bumpy reflection
1679 void idMaterial::AddImplicitStages( const textureRepeat_t trpDefault /* = TR_REPEAT */ ) {
1682 bool hasDiffuse = false;
1683 bool hasSpecular = false;
1684 bool hasBump = false;
1685 bool hasReflection = false;
1687 for ( int i = 0 ; i < numStages ; i++ ) {
1688 if ( pd->parseStages[i].lighting == SL_BUMP ) {
1691 if ( pd->parseStages[i].lighting == SL_DIFFUSE ) {
1694 if ( pd->parseStages[i].lighting == SL_SPECULAR ) {
1697 if ( pd->parseStages[i].texture.texgen == TG_REFLECT_CUBE ) {
1698 hasReflection = true;
1702 // if it doesn't have an interaction at all, don't add anything
1703 if ( !hasBump && !hasDiffuse && !hasSpecular ) {
1707 if ( numStages == MAX_SHADER_STAGES ) {
1712 idStr::snPrintf( buffer, sizeof( buffer ), "blend bumpmap\nmap _flat\n}\n" );
1713 newSrc.LoadMemory( buffer, strlen(buffer), "bumpmap" );
1714 newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
1715 ParseStage( newSrc, trpDefault );
1716 newSrc.FreeSource();
1719 if ( !hasDiffuse && !hasSpecular && !hasReflection ) {
1720 idStr::snPrintf( buffer, sizeof( buffer ), "blend diffusemap\nmap _white\n}\n" );
1721 newSrc.LoadMemory( buffer, strlen(buffer), "diffusemap" );
1722 newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
1723 ParseStage( newSrc, trpDefault );
1724 newSrc.FreeSource();
1731 idMaterial::SortInteractionStages
1733 The renderer expects bump, then diffuse, then specular
1734 There can be multiple bump maps, followed by additional
1735 diffuse and specular stages, which allows cross-faded bump mapping.
1737 Ambient stages can be interspersed anywhere, but they are
1738 ignored during interactions, and all the interaction
1739 stages are ignored during ambient drawing.
1742 void idMaterial::SortInteractionStages() {
1745 for ( int i = 0 ; i < numStages ; i = j ) {
1746 // find the next bump map
1747 for ( j = i + 1 ; j < numStages ; j++ ) {
1748 if ( pd->parseStages[j].lighting == SL_BUMP ) {
1749 // if the very first stage wasn't a bumpmap,
1750 // this bumpmap is part of the first group
1751 if ( pd->parseStages[i].lighting != SL_BUMP ) {
1758 // bubble sort everything bump / diffuse / specular
1759 for ( int l = 1 ; l < j-i ; l++ ) {
1760 for ( int k = i ; k < j-l ; k++ ) {
1761 if ( pd->parseStages[k].lighting > pd->parseStages[k+1].lighting ) {
1764 temp = pd->parseStages[k];
1765 pd->parseStages[k] = pd->parseStages[k+1];
1766 pd->parseStages[k+1] = temp;
1775 idMaterial::ParseMaterial
1777 The current text pointer is at the explicit text definition of the
1778 Parse it into the global material variable. Later functions will optimize it.
1780 If there is any error during parsing, defaultShader will be set.
1783 void idMaterial::ParseMaterial( idLexer &src ) {
1794 numRegisters = EXP_REG_NUM_PREDEFINED; // leave space for the parms to be copied in
1795 for ( i = 0 ; i < numRegisters ; i++ ) {
1796 pd->registerIsTemporary[i] = true; // they aren't constants that can be folded
1801 textureRepeat_t trpDefault = TR_REPEAT; // allow a global setting for repeat
1804 if ( TestMaterialFlag( MF_DEFAULTED ) ) { // we have a parse error
1807 if ( !src.ExpectAnyToken( &token ) ) {
1808 SetMaterialFlag( MF_DEFAULTED );
1812 // end of material definition
1813 if ( token == "}" ) {
1816 else if ( !token.Icmp( "qer_editorimage") ) {
1817 src.ReadTokenOnLine( &token );
1818 editorImageName = token.c_str();
1819 src.SkipRestOfLine();
1823 else if ( !token.Icmp( "description") ) {
1824 src.ReadTokenOnLine( &token );
1825 desc = token.c_str();
1828 // check for the surface / content bit flags
1829 else if ( CheckSurfaceParm( &token ) ) {
1835 else if ( !token.Icmp( "polygonOffset" ) ) {
1836 SetMaterialFlag( MF_POLYGONOFFSET );
1837 if ( !src.ReadTokenOnLine( &token ) ) {
1841 // explict larger (or negative) offset
1842 polygonOffset = token.GetFloatValue();
1846 else if ( !token.Icmp( "noShadows" ) ) {
1847 SetMaterialFlag( MF_NOSHADOWS );
1850 else if ( !token.Icmp( "suppressInSubview" ) ) {
1851 suppressInSubview = true;
1854 else if ( !token.Icmp( "portalSky" ) ) {
1859 else if ( !token.Icmp( "noSelfShadow" ) ) {
1860 SetMaterialFlag( MF_NOSELFSHADOW );
1864 else if ( !token.Icmp( "noPortalFog" ) ) {
1865 SetMaterialFlag( MF_NOPORTALFOG );
1868 // forceShadows allows nodraw surfaces to cast shadows
1869 else if ( !token.Icmp( "forceShadows" ) ) {
1870 SetMaterialFlag( MF_FORCESHADOWS );
1873 // overlay / decal suppression
1874 else if ( !token.Icmp( "noOverlays" ) ) {
1875 allowOverlays = false;
1878 // moster blood overlay forcing for alpha tested or translucent surfaces
1879 else if ( !token.Icmp( "forceOverlays" ) ) {
1880 pd->forceOverlays = true;
1884 else if ( !token.Icmp( "translucent" ) ) {
1885 coverage = MC_TRANSLUCENT;
1888 // global zero clamp
1889 else if ( !token.Icmp( "zeroclamp" ) ) {
1890 trpDefault = TR_CLAMP_TO_ZERO;
1894 else if ( !token.Icmp( "clamp" ) ) {
1895 trpDefault = TR_CLAMP;
1899 else if ( !token.Icmp( "alphazeroclamp" ) ) {
1900 trpDefault = TR_CLAMP_TO_ZERO;
1903 // forceOpaque is used for skies-behind-windows
1904 else if ( !token.Icmp( "forceOpaque" ) ) {
1905 coverage = MC_OPAQUE;
1909 else if ( !token.Icmp( "twoSided" ) ) {
1910 cullType = CT_TWO_SIDED;
1911 // twoSided implies no-shadows, because the shadow
1912 // volume would be coplanar with the surface, giving depth fighting
1913 // we could make this no-self-shadows, but it may be more important
1914 // to receive shadows from no-self-shadow monsters
1915 SetMaterialFlag( MF_NOSHADOWS );
1918 else if ( !token.Icmp( "backSided" ) ) {
1919 cullType = CT_BACK_SIDED;
1920 // the shadow code doesn't handle this, so just disable shadows.
1921 // We could fix this in the future if there was a need.
1922 SetMaterialFlag( MF_NOSHADOWS );
1925 else if ( !token.Icmp( "fogLight" ) ) {
1930 else if ( !token.Icmp( "blendLight" ) ) {
1935 else if ( !token.Icmp( "ambientLight" ) ) {
1936 ambientLight = true;
1940 else if ( !token.Icmp( "mirror" ) ) {
1942 coverage = MC_OPAQUE;
1946 else if ( !token.Icmp( "noFog" ) ) {
1950 // unsmoothedTangents
1951 else if ( !token.Icmp( "unsmoothedTangents" ) ) {
1952 unsmoothedTangents = true;
1955 // lightFallofImage <imageprogram>
1956 // specifies the image to use for the third axis of projected
1958 else if ( !token.Icmp( "lightFalloffImage" ) ) {
1959 str = R_ParsePastImageProgram( src );
1962 copy = str; // so other things don't step on it
1963 lightFalloffImage = globalImages->ImageFromFile( copy, TF_DEFAULT, false, TR_CLAMP /* TR_CLAMP_TO_ZERO */, TD_DEFAULT );
1966 // guisurf <guifile> | guisurf entity
1967 // an entity guisurf must have an idUserInterface
1968 // specified in the renderEntity
1969 else if ( !token.Icmp( "guisurf" ) ) {
1970 src.ReadTokenOnLine( &token );
1971 if ( !token.Icmp( "entity" ) ) {
1973 } else if ( !token.Icmp( "entity2" ) ) {
1975 } else if ( !token.Icmp( "entity3" ) ) {
1978 gui = uiManager->FindGui( token.c_str(), true );
1983 else if ( !token.Icmp( "sort" ) ) {
1987 // spectrum <integer>
1988 else if ( !token.Icmp( "spectrum" ) ) {
1989 src.ReadTokenOnLine( &token );
1990 spectrum = atoi( token.c_str() );
1993 // deform < sprite | tube | flare >
1994 else if ( !token.Icmp( "deform" ) ) {
1998 // decalInfo <staySeconds> <fadeSeconds> ( <start rgb> ) ( <end rgb> )
1999 else if ( !token.Icmp( "decalInfo" ) ) {
2000 ParseDecalInfo( src );
2003 // renderbump <args...>
2004 else if ( !token.Icmp( "renderbump") ) {
2005 src.ParseRestOfLine( renderBump );
2008 // diffusemap for stage shortcut
2009 else if ( !token.Icmp( "diffusemap" ) ) {
2010 str = R_ParsePastImageProgram( src );
2011 idStr::snPrintf( buffer, sizeof( buffer ), "blend diffusemap\nmap %s\n}\n", str );
2012 newSrc.LoadMemory( buffer, strlen(buffer), "diffusemap" );
2013 newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
2014 ParseStage( newSrc, trpDefault );
2015 newSrc.FreeSource();
2018 // specularmap for stage shortcut
2019 else if ( !token.Icmp( "specularmap" ) ) {
2020 str = R_ParsePastImageProgram( src );
2021 idStr::snPrintf( buffer, sizeof( buffer ), "blend specularmap\nmap %s\n}\n", str );
2022 newSrc.LoadMemory( buffer, strlen(buffer), "specularmap" );
2023 newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
2024 ParseStage( newSrc, trpDefault );
2025 newSrc.FreeSource();
2028 // normalmap for stage shortcut
2029 else if ( !token.Icmp( "bumpmap" ) ) {
2030 str = R_ParsePastImageProgram( src );
2031 idStr::snPrintf( buffer, sizeof( buffer ), "blend bumpmap\nmap %s\n}\n", str );
2032 newSrc.LoadMemory( buffer, strlen(buffer), "bumpmap" );
2033 newSrc.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
2034 ParseStage( newSrc, trpDefault );
2035 newSrc.FreeSource();
2038 // DECAL_MACRO for backwards compatibility with the preprocessor macros
2039 else if ( !token.Icmp( "DECAL_MACRO" ) ) {
2041 SetMaterialFlag( MF_POLYGONOFFSET );
2045 surfaceFlags |= SURF_DISCRETE;
2046 contentFlags &= ~CONTENTS_SOLID;
2052 SetMaterialFlag( MF_NOSHADOWS );
2055 else if ( token == "{" ) {
2056 // create the new stage
2057 ParseStage( src, trpDefault );
2061 common->Warning( "unknown general material parameter '%s' in '%s'", token.c_str(), GetName() );
2062 SetMaterialFlag( MF_DEFAULTED );
2067 // add _flat or _white stages if needed
2068 AddImplicitStages();
2070 // order the diffuse / bump / specular stages properly
2071 SortInteractionStages();
2073 // if we need to do anything with normals (lighting or environment mapping)
2074 // and two sided lighting was asked for, flag
2075 // shouldCreateBackSides() and change culling back to single sided,
2076 // so we get proper tangent vectors on both sides
2078 // we can't just call ReceivesLighting(), because the stages are still
2079 // in temporary form
2080 if ( cullType == CT_TWO_SIDED ) {
2081 for ( i = 0 ; i < numStages ; i++ ) {
2082 if ( pd->parseStages[i].lighting != SL_AMBIENT || pd->parseStages[i].texture.texgen != TG_EXPLICIT ) {
2083 if ( cullType == CT_TWO_SIDED ) {
2084 cullType = CT_FRONT_SIDED;
2085 shouldCreateBackSides = true;
2092 // currently a surface can only have one unique texgen for all the stages on old hardware
2093 texgen_t firstGen = TG_EXPLICIT;
2094 for ( i = 0; i < numStages; i++ ) {
2095 if ( pd->parseStages[i].texture.texgen != TG_EXPLICIT ) {
2096 if ( firstGen == TG_EXPLICIT ) {
2097 firstGen = pd->parseStages[i].texture.texgen;
2098 } else if ( firstGen != pd->parseStages[i].texture.texgen ) {
2099 common->Warning( "material '%s' has multiple stages with a texgen", GetName() );
2107 =========================
2109 =========================
2111 void idMaterial::SetGui( const char *_gui ) const {
2112 gui = uiManager->FindGui( _gui, true, false, true );
2116 =========================
2119 Parses the current material definition and finds all necessary images.
2120 =========================
2122 bool idMaterial::Parse( const char *text, const int textLength ) {
2125 mtrParsingData_t parsingData;
2127 src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
2128 src.SetFlags( DECL_LEXER_FLAGS );
2129 src.SkipUntilString( "{" );
2131 // reset to the unparsed state
2134 memset( &parsingData, 0, sizeof( parsingData ) );
2136 pd = &parsingData; // this is only valid during parse
2139 ParseMaterial( src );
2141 // if we are doing an fs_copyfiles, also reference the editorImage
2142 if ( cvarSystem->GetCVarInteger( "fs_copyFiles" ) ) {
2147 // count non-lit stages
2148 numAmbientStages = 0;
2150 for ( i = 0 ; i < numStages ; i++ ) {
2151 if ( pd->parseStages[i].lighting == SL_AMBIENT ) {
2156 // see if there is a subview stage
2157 if ( sort == SS_SUBVIEW ) {
2161 for ( i = 0 ; i < numStages ; i++ ) {
2162 if ( pd->parseStages[i].texture.dynamic ) {
2168 // automatically determine coverage if not explicitly set
2169 if ( coverage == MC_BAD ) {
2170 // automatically set MC_TRANSLUCENT if we don't have any interaction stages and
2171 // the first stage is blended and not an alpha test mask or a subview
2174 coverage = MC_TRANSLUCENT;
2175 } else if ( numStages != numAmbientStages ) {
2176 // we have an interaction draw
2177 coverage = MC_OPAQUE;
2179 ( pd->parseStages[0].drawStateBits & GLS_DSTBLEND_BITS ) != GLS_DSTBLEND_ZERO ||
2180 ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_DST_COLOR ||
2181 ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_ONE_MINUS_DST_COLOR ||
2182 ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_DST_ALPHA ||
2183 ( pd->parseStages[0].drawStateBits & GLS_SRCBLEND_BITS ) == GLS_SRCBLEND_ONE_MINUS_DST_ALPHA
2185 // blended with the destination
2186 coverage = MC_TRANSLUCENT;
2188 coverage = MC_OPAQUE;
2192 // translucent automatically implies noshadows
2193 if ( coverage == MC_TRANSLUCENT ) {
2194 SetMaterialFlag( MF_NOSHADOWS );
2196 // mark the contents as opaque
2197 contentFlags |= CONTENTS_OPAQUE;
2200 // if we are translucent, draw with an alpha in the editor
2201 if ( coverage == MC_TRANSLUCENT ) {
2207 // the sorts can make reasonable defaults
2208 if ( sort == SS_BAD ) {
2209 if ( TestMaterialFlag(MF_POLYGONOFFSET) ) {
2211 } else if ( coverage == MC_TRANSLUCENT ) {
2218 // anything that references _currentRender will automatically get sort = SS_POST_PROCESS
2219 // and coverage = MC_TRANSLUCENT
2221 for ( i = 0 ; i < numStages ; i++ ) {
2222 shaderStage_t *pStage = &pd->parseStages[i];
2223 if ( pStage->texture.image == globalImages->currentRenderImage ) {
2224 if ( sort != SS_PORTAL_SKY ) {
2225 sort = SS_POST_PROCESS;
2226 coverage = MC_TRANSLUCENT;
2230 if ( pStage->newStage ) {
2231 for ( int j = 0 ; j < pStage->newStage->numFragmentProgramImages ; j++ ) {
2232 if ( pStage->newStage->fragmentProgramImages[j] == globalImages->currentRenderImage ) {
2233 if ( sort != SS_PORTAL_SKY ) {
2234 sort = SS_POST_PROCESS;
2235 coverage = MC_TRANSLUCENT;
2244 // set the drawStateBits depth flags
2245 for ( i = 0 ; i < numStages ; i++ ) {
2246 shaderStage_t *pStage = &pd->parseStages[i];
2247 if ( sort == SS_POST_PROCESS ) {
2248 // post-process effects fill the depth buffer as they draw, so only the
2249 // topmost post-process effect is rendered
2250 pStage->drawStateBits |= GLS_DEPTHFUNC_LESS;
2251 } else if ( coverage == MC_TRANSLUCENT || pStage->ignoreAlphaTest ) {
2252 // translucent surfaces can extend past the exactly marked depth buffer
2253 pStage->drawStateBits |= GLS_DEPTHFUNC_LESS | GLS_DEPTHMASK;
2255 // opaque and perforated surfaces must exactly match the depth buffer,
2256 // which gets alpha test correct
2257 pStage->drawStateBits |= GLS_DEPTHFUNC_EQUAL | GLS_DEPTHMASK;
2261 // determine if this surface will accept overlays / decals
2263 if ( pd->forceOverlays ) {
2264 // explicitly flaged in material definition
2265 allowOverlays = true;
2268 allowOverlays = false;
2270 if ( Coverage() != MC_OPAQUE ) {
2271 allowOverlays = false;
2273 if ( GetSurfaceFlags() & SURF_NOIMPACT ) {
2274 allowOverlays = false;
2278 // add a tiny offset to the sort orders, so that different materials
2279 // that have the same sort value will at least sort consistantly, instead
2280 // of flickering back and forth
2281 /* this messed up in-game guis
2282 if ( sort != SS_SUBVIEW ) {
2287 for ( int i = 0 ; i < l ; i++ ) {
2290 sort += hash * 0.01;
2295 stages = (shaderStage_t *)R_StaticAlloc( numStages * sizeof( stages[0] ) );
2296 memcpy( stages, pd->parseStages, numStages * sizeof( stages[0] ) );
2300 ops = (expOp_t *)R_StaticAlloc( numOps * sizeof( ops[0] ) );
2301 memcpy( ops, pd->shaderOps, numOps * sizeof( ops[0] ) );
2304 if ( numRegisters ) {
2305 expressionRegisters = (float *)R_StaticAlloc( numRegisters * sizeof( expressionRegisters[0] ) );
2306 memcpy( expressionRegisters, pd->shaderRegisters, numRegisters * sizeof( expressionRegisters[0] ) );
2309 // see if the registers are completely constant, and don't need to be evaluated
2311 CheckForConstantRegisters();
2313 pd = NULL; // the pointer will be invalid after exiting this function
2316 if ( TestMaterialFlag( MF_DEFAULTED ) ) {
2345 void idMaterial::Print() const {
2348 for ( i = EXP_REG_NUM_PREDEFINED ; i < GetNumRegisters() ; i++ ) {
2349 common->Printf( "register %i: %f\n", i, expressionRegisters[i] );
2351 common->Printf( "\n" );
2352 for ( i = 0 ; i < numOps ; i++ ) {
2353 const expOp_t *op = &ops[i];
2354 if ( op->opType == OP_TYPE_TABLE ) {
2355 common->Printf( "%i = %s[ %i ]\n", op->c, declManager->DeclByIndex( DECL_TABLE, op->a )->GetName(), op->b );
2357 common->Printf( "%i = %i %s %i\n", op->c, op->a, opNames[ op->opType ], op->b );
2367 bool idMaterial::Save( const char *fileName ) {
2368 return ReplaceSourceFileText();
2373 idMaterial::AddReference
2376 void idMaterial::AddReference() {
2379 for ( int i = 0; i < numStages; i++ ) {
2380 shaderStage_t *s = &stages[i];
2382 if ( s->texture.image ) {
2383 s->texture.image->AddReference();
2390 idMaterial::EvaluateRegisters
2392 Parameters are taken from the localSpace and the renderView,
2393 then all expressions are evaluated, leaving the material registers
2394 set to their apropriate values.
2397 void idMaterial::EvaluateRegisters( float *registers, const float shaderParms[MAX_ENTITY_SHADER_PARMS],
2398 const viewDef_t *view, idSoundEmitter *soundEmitter ) const {
2402 // copy the material constants
2403 for ( i = EXP_REG_NUM_PREDEFINED ; i < numRegisters ; i++ ) {
2404 registers[i] = expressionRegisters[i];
2407 // copy the local and global parameters
2408 registers[EXP_REG_TIME] = view->floatTime;
2409 registers[EXP_REG_PARM0] = shaderParms[0];
2410 registers[EXP_REG_PARM1] = shaderParms[1];
2411 registers[EXP_REG_PARM2] = shaderParms[2];
2412 registers[EXP_REG_PARM3] = shaderParms[3];
2413 registers[EXP_REG_PARM4] = shaderParms[4];
2414 registers[EXP_REG_PARM5] = shaderParms[5];
2415 registers[EXP_REG_PARM6] = shaderParms[6];
2416 registers[EXP_REG_PARM7] = shaderParms[7];
2417 registers[EXP_REG_PARM8] = shaderParms[8];
2418 registers[EXP_REG_PARM9] = shaderParms[9];
2419 registers[EXP_REG_PARM10] = shaderParms[10];
2420 registers[EXP_REG_PARM11] = shaderParms[11];
2421 registers[EXP_REG_GLOBAL0] = view->renderView.shaderParms[0];
2422 registers[EXP_REG_GLOBAL1] = view->renderView.shaderParms[1];
2423 registers[EXP_REG_GLOBAL2] = view->renderView.shaderParms[2];
2424 registers[EXP_REG_GLOBAL3] = view->renderView.shaderParms[3];
2425 registers[EXP_REG_GLOBAL4] = view->renderView.shaderParms[4];
2426 registers[EXP_REG_GLOBAL5] = view->renderView.shaderParms[5];
2427 registers[EXP_REG_GLOBAL6] = view->renderView.shaderParms[6];
2428 registers[EXP_REG_GLOBAL7] = view->renderView.shaderParms[7];
2431 for ( i = 0 ; i < numOps ; i++, op++ ) {
2432 switch( op->opType ) {
2434 registers[op->c] = registers[op->a] + registers[op->b];
2436 case OP_TYPE_SUBTRACT:
2437 registers[op->c] = registers[op->a] - registers[op->b];
2439 case OP_TYPE_MULTIPLY:
2440 registers[op->c] = registers[op->a] * registers[op->b];
2442 case OP_TYPE_DIVIDE:
2443 registers[op->c] = registers[op->a] / registers[op->b];
2446 b = (int)registers[op->b];
2448 registers[op->c] = (int)registers[op->a] % b;
2452 const idDeclTable *table = static_cast<const idDeclTable *>( declManager->DeclByIndex( DECL_TABLE, op->a ) );
2453 registers[op->c] = table->TableLookup( registers[op->b] );
2457 if ( soundEmitter ) {
2458 registers[op->c] = soundEmitter->CurrentAmplitude();
2460 registers[op->c] = 0;
2464 registers[op->c] = registers[ op->a ] > registers[op->b];
2467 registers[op->c] = registers[ op->a ] >= registers[op->b];
2470 registers[op->c] = registers[ op->a ] < registers[op->b];
2473 registers[op->c] = registers[ op->a ] <= registers[op->b];
2476 registers[op->c] = registers[ op->a ] == registers[op->b];
2479 registers[op->c] = registers[ op->a ] != registers[op->b];
2482 registers[op->c] = registers[ op->a ] && registers[op->b];
2485 registers[op->c] = registers[ op->a ] || registers[op->b];
2488 common->FatalError( "R_EvaluateExpression: bad opcode" );
2499 texgen_t idMaterial::Texgen() const {
2501 for ( int i = 0; i < numStages; i++ ) {
2502 if ( stages[ i ].texture.texgen != TG_EXPLICIT ) {
2503 return stages[ i ].texture.texgen;
2513 idMaterial::GetImageWidth
2516 int idMaterial::GetImageWidth( void ) const {
2517 assert( GetStage(0) && GetStage(0)->texture.image );
2518 return GetStage(0)->texture.image->uploadWidth;
2523 idMaterial::GetImageHeight
2526 int idMaterial::GetImageHeight( void ) const {
2527 assert( GetStage(0) && GetStage(0)->texture.image );
2528 return GetStage(0)->texture.image->uploadHeight;
2533 idMaterial::CinematicLength
2536 int idMaterial::CinematicLength() const {
2537 if ( !stages || !stages[0].texture.cinematic ) {
2540 return stages[0].texture.cinematic->AnimationLength();
2545 idMaterial::UpdateCinematic
2548 void idMaterial::UpdateCinematic( int time ) const {
2549 if ( !stages || !stages[0].texture.cinematic || !backEnd.viewDef ) {
2552 stages[0].texture.cinematic->ImageForTime( tr.primaryRenderView.time );
2557 idMaterial::CloseCinematic
2560 void idMaterial::CloseCinematic( void ) const {
2561 for( int i = 0; i < numStages; i++ ) {
2562 if ( stages[i].texture.cinematic ) {
2563 stages[i].texture.cinematic->Close();
2564 delete stages[i].texture.cinematic;
2565 stages[i].texture.cinematic = NULL;
2572 idMaterial::ResetCinematicTime
2575 void idMaterial::ResetCinematicTime( int time ) const {
2576 for( int i = 0; i < numStages; i++ ) {
2577 if ( stages[i].texture.cinematic ) {
2578 stages[i].texture.cinematic->ResetTime( time );
2585 idMaterial::ConstantRegisters
2588 const float *idMaterial::ConstantRegisters() const {
2589 if ( !r_useConstantMaterials.GetBool() ) {
2592 return constantRegisters;
2597 idMaterial::CheckForConstantRegisters
2599 As of 5/2/03, about half of the unique materials loaded on typical
2600 maps are constant, but 2/3 of the surface references are.
2601 This is probably an optimization of dubious value.
2604 static int c_constant, c_variable;
2605 void idMaterial::CheckForConstantRegisters() {
2606 if ( !pd->registersAreConstant ) {
2610 // evaluate the registers once, and save them
2611 constantRegisters = (float *)R_ClearedStaticAlloc( GetNumRegisters() * sizeof( float ) );
2613 float shaderParms[MAX_ENTITY_SHADER_PARMS];
2614 memset( shaderParms, 0, sizeof( shaderParms ) );
2616 memset( &viewDef, 0, sizeof( viewDef ) );
2618 EvaluateRegisters( constantRegisters, shaderParms, &viewDef, 0 );
2623 idMaterial::ImageName
2626 const char *idMaterial::ImageName( void ) const {
2627 if ( numStages == 0 ) {
2630 idImage *image = stages[0].texture.image;
2632 return image->imgName;
2639 idMaterial::SetImageClassifications
2641 Just for image resource tracking.
2644 void idMaterial::SetImageClassifications( int tag ) const {
2645 for ( int i = 0 ; i < numStages ; i++ ) {
2646 idImage *image = stages[i].texture.image;
2648 image->SetClassification( tag );
2658 size_t idMaterial::Size( void ) const {
2659 return sizeof( idMaterial );
2664 idMaterial::SetDefaultText
2667 bool idMaterial::SetDefaultText( void ) {
2668 // if there exists an image with the same name
2669 if ( 1 ) { //fileSystem->ReadFile( GetName(), NULL ) != -1 ) {
2670 char generated[2048];
2671 idStr::snPrintf( generated, sizeof( generated ),
2672 "material %s // IMPLICITLY GENERATED\n"
2680 "}\n", GetName(), GetName() );
2681 SetText( generated );
2690 idMaterial::DefaultDefinition
2693 const char *idMaterial::DefaultDefinition() const {
2697 "\t\t" "blend\tblend\n"
2698 "\t\t" "map\t\t_default\n"
2706 idMaterial::GetBumpStage
2709 const shaderStage_t *idMaterial::GetBumpStage( void ) const {
2710 for ( int i = 0 ; i < numStages ; i++ ) {
2711 if ( stages[i].lighting == SL_BUMP ) {
2720 idMaterial::ReloadImages
2723 void idMaterial::ReloadImages( bool force ) const
2725 for ( int i = 0 ; i < numStages ; i++ ) {
2726 if ( stages[i].newStage ) {
2727 for ( int j = 0 ; j < stages[i].newStage->numFragmentProgramImages ; j++ ) {
2728 if ( stages[i].newStage->fragmentProgramImages[j] ) {
2729 stages[i].newStage->fragmentProgramImages[j]->Reload( false, force );
2732 } else if ( stages[i].texture.image ) {
2733 stages[i].texture.image->Reload( false, force );