/// <summary>
/// Parses the material, if there are any errors during parsing the defaultShader will be set.
/// </summary>
/// <param name="lexer"></param>
private void ParseMaterial(idLexer lexer)
{
_registerCount = PredefinedRegisterCount; // leave space for the parms to be copied in.
for(int i = 0; i < _registerCount; i++)
{
_parsingData.RegisterIsTemporary[i] = true; // they aren't constants that can be folded.
}
TextureRepeat textureRepeatDefault = TextureRepeat.Repeat; // allow a global setting for repeat.
idToken token = null;
string tokenValue;
string tokenLower;
int count;
while(true)
{
if(TestMaterialFlag(Renderer.MaterialFlags.Defaulted) == true)
{
// we have a parse error.
return;
}
if((token = lexer.ExpectAnyToken()) == null)
{
this.MaterialFlag = MaterialFlags.Defaulted;
return;
}
tokenValue = token.ToString();
tokenLower = tokenValue.ToLower();
// end of material definition
if(tokenLower == "}")
{
break;
}
else if(tokenLower == "qer_editorimage")
{
token = lexer.ReadTokenOnLine();
_editorImageName = (token != null) ? token.ToString() : string.Empty;
lexer.SkipRestOfLine();
}
else if(tokenLower == "description")
{
token = lexer.ReadTokenOnLine();
_description = (token != null) ? token.ToString() : string.Empty;
}
// check for the surface / content bit flags.
else if(CheckSurfaceParameter(token) == true)
{
}
else if(tokenLower == "polygonoffset")
{
this.MaterialFlag = Renderer.MaterialFlags.PolygonOffset;
if((token = lexer.ReadTokenOnLine()) == null)
{
_polygonOffset = 1;
}
else
{
_polygonOffset = token.ToFloat();
}
}
// noshadow.
else if(tokenLower == "noshadows")
{
this.MaterialFlag = MaterialFlags.NoShadows;
}
else if(tokenLower == "suppressinsubview")
{
_suppressInSubview = true;
}
else if(tokenLower == "portalsky")
{
_portalSky = true;
}
else if(tokenLower == "noselfshadow")
{
this.MaterialFlag = Renderer.MaterialFlags.NoSelfShadow;
}
else if(tokenLower == "noportalfog")
{
this.MaterialFlag = Renderer.MaterialFlags.NoPortalFog;
}
// forceShadows allows nodraw surfaces to cast shadows.
else if(tokenLower == "forceshadows")
{
this.MaterialFlag = Renderer.MaterialFlags.ForceShadows;
}
// overlay / decal suppression.
else if(tokenLower == "nooverlays")
{
_allowOverlays = false;
}
// moster blood overlay forcing for alpha tested or translucent surfaces.
else if(tokenLower == "forceoverlays")
{
_parsingData.ForceOverlays = true;
}
else if(tokenLower == "translucent")
{
_coverage = MaterialCoverage.Translucent;
}
// global zero clamp.
else if(tokenLower == "zeroclamp")
{
textureRepeatDefault = TextureRepeat.ClampToZero;
}
// global clamp.
else if(tokenLower == "clamp")
{
textureRepeatDefault = TextureRepeat.Clamp;
}
// global clamp.
else if(tokenLower == "alphazeroclamp")
{
textureRepeatDefault = TextureRepeat.ClampToZero;
}
// forceOpaque is used for skies-behind-windows.
else if(tokenLower == "forceopaque")
{
_coverage = MaterialCoverage.Opaque;
}
else if(tokenLower == "twosided")
{
_cullType = CullType.TwoSided;
// twoSided implies no-shadows, because the shadow
// volume would be coplanar with the surface, giving depth fighting
// we could make this no-self-shadows, but it may be more important
// to receive shadows from no-self-shadow monsters.
this.MaterialFlag = Renderer.MaterialFlags.NoShadows;
}
else if(tokenLower == "backsided")
{
_cullType = CullType.Back;
// the shadow code doesn't handle this, so just disable shadows.
// We could fix this in the future if there was a need.
this.MaterialFlag = Renderer.MaterialFlags.NoShadows;
}
else if(tokenLower == "foglight")
{
_fogLight = true;
}
else if(tokenLower == "blendlight")
{
_blendLight = true;
}
else if(tokenLower == "ambientlight")
{
_ambientLight = true;
}
else if(tokenLower == "mirror")
{
_sort = (float) MaterialSort.Subview;
_coverage = MaterialCoverage.Opaque;
}
else if(tokenLower == "nofog")
{
_noFog = true;
}
else if(tokenLower == "unsmoothedtangents")
{
_unsmoothedTangents = true;
}
// lightFallofImage <imageprogram>
// specifies the image to use for the third axis of projected
// light volumes.
else if(tokenLower == "lightfalloffimage")
{
_lightFalloffImage = idE.ImageManager.ImageFromFile(ParsePastImageProgram(lexer), TextureFilter.Default, false, TextureRepeat.Clamp, TextureDepth.Default);
}
// guisurf <guifile> | guisurf entity
// an entity guisurf must have an idUserInterface
// specified in the renderEntity.
else if(tokenLower == "guisurf")
{
token = lexer.ReadTokenOnLine();
tokenLower = token.ToString().ToLower();
if(tokenLower == "entity")
{
_entityGui = 1;
}
else if(tokenLower == "entity2")
{
_entityGui = 2;
}
else if(tokenLower == "entity3")
{
_entityGui = 3;
}
else
{
_userInterface = idE.UIManager.FindInterface(token.ToString(), true);
}
}
// sort.
else if(tokenLower == "sort")
{
ParseSort(lexer);
}
// spectrum <integer>.
else if(tokenLower == "spectrum")
{
int.TryParse(lexer.ReadTokenOnLine().ToString(), out _spectrum);
}
// deform < sprite | tube | flare >.
else if(tokenLower == "deform")
{
ParseDeform(lexer);
}
// decalInfo <staySeconds> <fadeSeconds> (<start rgb>) (<end rgb>).
else if(tokenLower == "decalinfo")
{
ParseDecalInfo(lexer);
}
// renderbump <args...>.
else if(tokenLower == "renderbump")
{
_renderBump = lexer.ParseRestOfLine();
}
// diffusemap for stage shortcut.
else if(tokenLower == "diffusemap")
{
idLexer newLexer = new idLexer(LexerOptions.NoFatalErrors | LexerOptions.NoStringConcatination | LexerOptions.NoStringEscapeCharacters | LexerOptions.AllowPathNames);
newLexer.LoadMemory(string.Format("blend diffusemap\nmap {0}\n}}\n", ParsePastImageProgram(lexer)), "diffusemap");
ParseStage(newLexer, textureRepeatDefault);
}
// specularmap for stage shortcut.
else if(tokenLower == "specularmap")
{
idLexer newLexer = new idLexer(LexerOptions.NoFatalErrors | LexerOptions.NoStringConcatination | LexerOptions.NoStringEscapeCharacters | LexerOptions.AllowPathNames);
newLexer.LoadMemory(string.Format("blend specularmap\nmap {0}\n}}\n", ParsePastImageProgram(lexer)), "specularmap");
ParseStage(newLexer, textureRepeatDefault);
}
// normalmap for stage shortcut.
else if(tokenLower == "bumpmap")
{
idLexer newLexer = new idLexer(LexerOptions.NoFatalErrors | LexerOptions.NoStringConcatination | LexerOptions.NoStringEscapeCharacters | LexerOptions.AllowPathNames);
newLexer.LoadMemory(string.Format("blend bumpmap\nmap {0}\n}}\n", ParsePastImageProgram(lexer)), "bumpmap");
ParseStage(newLexer, textureRepeatDefault);
}
// DECAL_MACRO for backwards compatibility with the preprocessor macros.
else if(tokenLower == "decal_macro")
{
// polygonOffset
this.MaterialFlag = Renderer.MaterialFlags.PolygonOffset;
_polygonOffset = -1;
// discrete
_surfaceFlags |= SurfaceFlags.Discrete;
_contentFlags &= ~ContentFlags.Solid;
// sort decal.
_sort = (float) MaterialSort.Decal;
// noShadows
this.MaterialFlag = Renderer.MaterialFlags.NoShadows;
}
else if(tokenValue == "{")
{
// create the new stage.
ParseStage(lexer, textureRepeatDefault);
}
else
{
idConsole.WriteLine("unknown general material parameter '{0}' in '{1}'", tokenValue, this.Name);
return;
}
}
// add _flat or _white stages if needed.
AddImplicitStages();
// order the diffuse / bump / specular stages properly.
SortInteractionStages();
// if we need to do anything with normals (lighting or environment mapping)
// and two sided lighting was asked for, flag
// shouldCreateBackSides() and change culling back to single sided,
// so we get proper tangent vectors on both sides.
// we can't just call ReceivesLighting(), because the stages are still
// in temporary form.
if(_cullType == CullType.TwoSided)
{
count = _parsingData.Stages.Count;
for(int i = 0; i < count; i++)
{
if((_parsingData.Stages[i].Lighting != StageLighting.Ambient) || (_parsingData.Stages[i].Texture.TextureCoordinates != TextureCoordinateGeneration.Explicit))
{
if(_cullType == CullType.TwoSided)
{
_cullType = CullType.Front;
_shouldCreateBackSides = true;
}
break;
}
}
}
// currently a surface can only have one unique texgen for all the stages on old hardware.
TextureCoordinateGeneration firstGen = TextureCoordinateGeneration.Explicit;
count = _parsingData.Stages.Count;
for(int i = 0; i < count; i++)
{
if(_parsingData.Stages[i].Texture.TextureCoordinates != TextureCoordinateGeneration.Explicit)
{
if(firstGen == TextureCoordinateGeneration.Explicit)
{
firstGen = _parsingData.Stages[i].Texture.TextureCoordinates;
}
else if(firstGen != _parsingData.Stages[i].Texture.TextureCoordinates)
{
idConsole.Warning("material '{0}' has multiple stages with a texgen", this.Name);
break;
}
}
}
}