/// <summary> /// Processes a line that describes how to blend this stage and the last one together. /// </summary> private static void ProcessBlendFunc(string line, ref Q3BSPMaterialStageContent currentStage) { #region Handle one of the "default" blendfuncs if (line.ToLower() == "add") { currentStage.SetBlendFunction(Q3BSPBlendFuncFactor.One, Q3BSPBlendFuncFactor.One); return; } if (line.ToLower() == "filter") { currentStage.SetBlendFunction(Q3BSPBlendFuncFactor.Destination, Q3BSPBlendFuncFactor.Zero); return; } if (line.ToLower() == "blend") { currentStage.SetBlendFunction(Q3BSPBlendFuncFactor.SourceAlpha, Q3BSPBlendFuncFactor.InverseSourceAlpha); return; } #endregion string[] split = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // Something is malformed if (split.Length != 2) { currentStage.SetBlendFunction(Q3BSPBlendFuncFactor.Invalid, Q3BSPBlendFuncFactor.Invalid); return; } currentStage.SetBlendFunction(ParseQ3BSPBlendFuncFactor(split[0]), ParseQ3BSPBlendFuncFactor(split[1])); }
/// <summary> /// Processes a line that describes a tcMod operation. /// </summary> private static Q3BSPMaterialStageTcMod ParseTcMod(string line, ref Q3BSPMaterialStageContent currentStage) { Q3BSPTcModFunction function = Q3BSPTcModFunction.Invalid; float[] data = new float[] { }; #region Scale if (line.Length > 4 && line.Substring(0, 5) == "scale") { string[] split = line.Split(' '); data = new float[] { 1.0f / float.Parse(split[1]), 1.0f / float.Parse(split[2]) }; function = Q3BSPTcModFunction.Scale; // skies seem to work backwards for some reason. if (currentStage.Parent != null && currentStage.Parent.IsSky) { data[0] = float.Parse(split[1]); data[1] = float.Parse(split[2]); } } #endregion #region Scroll else if (line.Length > 5 && line.Substring(0, 6) == "scroll") { string[] split = line.Split(' '); data = new float[] { float.Parse(split[1]), float.Parse(split[2]) }; function = Q3BSPTcModFunction.Scroll; } #endregion #region Rotate else if (line.Length > 5 && line.Substring(0, 6) == "rotate") { string[] split = line.Split(' '); data = new float[] { MathHelper.ToRadians(float.Parse(split[1])) }; function = Q3BSPTcModFunction.Rotate; } #endregion #region Turbulence else if (line.Length > 4 && line.Substring(0, 4) == "turb") { string[] split = line.Split(' '); data = new float[] { float.Parse(split[2]), float.Parse(split[3]), float.Parse(split[4]) }; function = Q3BSPTcModFunction.Turbulence; } #endregion return(new Q3BSPMaterialStageTcMod(function, data)); }
string WriteTcModEffectCode(Q3BSPMaterialStageContent stage) { if (stage.TcModStatements == null || stage.TcModStatements.Length == 0) { return(""); } StringBuilder sb = new StringBuilder(); foreach (Q3BSPMaterialStageTcMod tcMod in stage.TcModStatements) { string s = "output." + stage.StageName + ".uv.x"; string t = "output." + stage.StageName + ".uv.y"; switch (tcMod.Function) { case Q3BSPTcModFunction.Scale: sb.AppendLine("\t" + s + " *= " + tcMod.Data[0] + ";"); sb.AppendLine("\t" + t + " *= " + tcMod.Data[1] + ";"); break; case Q3BSPTcModFunction.Scroll: sb.AppendLine("\t" + s + " += " + tcMod.Data[0] + " * gameTime;"); sb.AppendLine("\t" + t + " += " + tcMod.Data[1] + " * gameTime;"); break; case Q3BSPTcModFunction.Turbulence: sb.AppendLine("\t" + s + " += 1 - sin(((((input.position.x + input.position.z) * 0.001 + (" + tcMod.Data[2] + " * gameTime))) % 1025) * " + MathHelper.TwoPi + ") * " + tcMod.Data[0] + ";"); sb.AppendLine("\t" + t + " += 1 - sin(((((input.position.y) * 0.001 + (" + tcMod.Data[2] + " * gameTime))) % 1025) * " + MathHelper.TwoPi + ") * " + tcMod.Data[0] + ";"); break; case Q3BSPTcModFunction.Rotate: string cosValue = "cos(" + tcMod.Data[0] + " * gameTime)"; string sinValue = "sin(" + tcMod.Data[0] + " * gameTime)"; sb.AppendLine("\t float s_" + stage.StageName + " = " + s + ";"); sb.AppendLine("\t float t_" + stage.StageName + " = " + t + ";"); sb.AppendLine("\t" + s + " = s_" + stage.StageName + "*" + cosValue + " + t_" + stage.StageName + " * -" + sinValue + " + (0.5 - 0.5 * " + cosValue + "+ 0.5 *" + sinValue + ");"); sb.AppendLine("\t" + t + " = s_" + stage.StageName + "*" + sinValue + " + t_" + stage.StageName + " * " + cosValue + " + (0.5 - 0.5 * " + sinValue + "- 0.5 *" + cosValue + ");"); break; default: sb.AppendLine("\t// Invalid or unparsed tcMod statement."); break; } } return(sb.ToString()); }
public override TImport Import(string filename, ContentImporterContext context) { //System.Diagnostics.Debugger.Launch(); TImport shaderList = new TImport(); StreamReader sr = new StreamReader(filename); Q3BSPMaterialContent shader = new Q3BSPMaterialContent(); Q3BSPMaterialStageContent currentStage = new Q3BSPMaterialStageContent(null); string shaderName = ""; bool insideShaderBlock = false; bool insideStageBlock = false; List <Q3BSPMaterialStageTcMod> currentStageTcMods = new List <Q3BSPMaterialStageTcMod>(); while (!sr.EndOfStream) { string line = sr.ReadLine(); int comment; #region Clean up line and throw out useless lines // Remove comments and whitespace if ((comment = line.IndexOf("//")) > -1) { line = line.Substring(0, comment); } line = line.Trim(); // Line is empty don't process it if (line == "") { continue; } // Line is important only when the BSP is compiled, don't process it if (line.Length > 3 && line.Substring(0, 4) == "qer_") { continue; } if (line.Length > 4 && line.Substring(0, 5) == "q3map") { continue; } if (line.Length > 10 && line.Substring(0, 11) == "surfaceparm") { continue; } #endregion #region Read lines outside any blocks if (!insideShaderBlock) { if (line.Substring(0, 1) == "{") { if (shaderName == "") { throw new Exception("Error loading .shader: there is a block without a shader name."); } shader = new Q3BSPMaterialContent(); shader.shaderName = shaderName; shader.stages = new List <Q3BSPMaterialStageContent>(); insideShaderBlock = true; } else { if (shaderName != "") { throw new Exception("Error loading .shader: Shader " + shaderName + " has no information describing it."); } shaderName = line; } } #endregion #region Read lines inside the shader block but outside the stage blocks else if (insideShaderBlock && !insideStageBlock) { if (line.Substring(0, 1) == "{") { insideStageBlock = true; currentStage = new Q3BSPMaterialStageContent(shader); currentStageTcMods.Clear(); } else if (line.Length > 8 && line.Substring(0, 8) == "skyparms") { ProcessSkyparms(line.Substring(8).Trim(), ref shader); } else if (line.Substring(0, 1) == "}") { #region If no shader stages are defined, this is a very basic shader and uses the shader name as the sole texture map if (shader.stages.Count < 1) { currentStage = new Q3BSPMaterialStageContent(shader); currentStage.TextureEffectParameterName = shader.shaderName.Substring(shader.shaderName.LastIndexOf('/') + 1); currentStage.TextureFilename = shader.shaderName; currentStageTcMods.Clear(); currentStage.Finalize(ref currentStageTcMods); shader.stages.Add(currentStage); currentStage = new Q3BSPMaterialStageContent(shader); currentStage.TextureEffectParameterName = "lightmap"; currentStage.TextureFilename = "lightmap"; currentStage.IsLightmapStage = true; ProcessBlendFunc("filter", ref currentStage); currentStage.Finalize(ref currentStageTcMods); shader.stages.Add(currentStage); } #endregion shaderList.Add(shader); shader = null; shaderName = ""; insideShaderBlock = false; } } #endregion #region Read lines inside stage blocks else if (insideStageBlock) { if (line.Length > 2 && line.Substring(0, 3) == "map") { string textureFilename = line.Substring(4); // Account for special texures (ie $lightmap) if (textureFilename.Substring(0, 1) == "$") { if (String.Compare(textureFilename.ToLower(), "$lightmap", true) == 0) { currentStage.IsLightmapStage = true; } textureFilename = textureFilename.Substring(1); } if (textureFilename.LastIndexOf('.') > -1) { textureFilename = textureFilename.Substring(0, textureFilename.LastIndexOf('.')); } currentStage.TextureFilename = textureFilename; } /* * else if (line.Length > 7 && line.Substring(0, 7).ToLower() == "animmap") * { * string[] arguments = line.Split(' '); * string textureFilename = arguments[2]; * if (textureFilename.LastIndexOf('.') > -1) * { * textureFilename = textureFilename.Substring(0, textureFilename.LastIndexOf('.')); * } * currentStage.TextureFilename = textureFilename; * } */ else if (line.Length > 8 && line.Substring(0, 9).ToLower() == "blendfunc") { ProcessBlendFunc(line.Substring(9).Trim(), ref currentStage); } else if (line.Length > 4 && line.Substring(0, 5).ToLower() == "tcmod") { Q3BSPMaterialStageTcMod tcMod = ParseTcMod(line.Substring(5).Trim(), ref currentStage); currentStageTcMods.Add(tcMod); // These tcmods require a time variable in their function if (tcMod.Function == Q3BSPTcModFunction.Scroll || tcMod.Function == Q3BSPTcModFunction.Rotate || tcMod.Function == Q3BSPTcModFunction.Turbulence) { shader.NeedsTime = true; } } /* * else if (line.Length > 5 && line.Substring(0, 6).ToLower() == "rgbgen") * { * currentStage.TextureFilename = line.Substring(7); * } */ else if (line.Substring(0, 1) == "}") { currentStage.Finalize(ref currentStageTcMods); shader.stages.Add(currentStage); insideStageBlock = false; } } #endregion } return(shaderList); }