Пример #1
0
        /// <summary>
        /// Processes a line that describes how a skybox should look.
        /// </summary>
        private static void ProcessSkyparms(string line, ref Q3BSPMaterialContent shader)
        {
            shader.IsSky = true;

            string[] split = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (split[0] != "-")
            {
                shader.FarBoxName = split[0];
            }
            writer.WriteLine(split[0] + "" + split[1] + "" + split[2]);
            writer.Flush();
            if (split[1] != "-")
            {
                shader.SkyHeight = int.Parse(split[1]);
            }
            if (split[2] != "-")
            {
                shader.NearBoxName = split[2];
            }
        }
Пример #2
0
        string GenerateEffectFromShader(Q3BSPMaterialContent shader, StreamWriter sw)
        {
            StringBuilder sb = new StringBuilder();

            int stageNumber    = 0;
            int texCoordNumber = 0;

            string externs             = "";
            string samplers            = "";
            string vertexShaderInput   = "";
            string vertexShaderOutput  = "";
            string stageInputStructs   = "";
            string vertexShaderProgram = "";
            string pixelShaderProgram  = "";
            string technique           = "";

            #region Generate Generic External Variables
            sb.AppendLine("// Generic Externs");
            sb.AppendLine("uniform float4x4 worldViewProj;");
            sb.AppendLine("uniform float gameTime;");
            externs += sb.ToString();
            sb.Remove(0, sb.ToString().Length);
            #endregion

            foreach (Q3BSPMaterialStageContent stage in shader.stages)
            {
                stage.InitializeEffectNames(stageNumber);

                #region Generate External Variables
                sb.AppendLine("// " + stage.StageName + "'s externs");
                sb.AppendLine("uniform texture " + stage.TextureEffectParameterName + ";");
                sb.AppendLine("uniform float3x2 " + stage.TcModName + ";");
                externs += sb.ToString();

                sb.Remove(0, sb.ToString().Length);
                #endregion

                #region Generate Samplers
                sb.AppendLine("sampler " + stage.SamplerName + " = sampler_state");
                sb.AppendLine("{");
                sb.AppendLine("\tTexture = <" + stage.TextureEffectParameterName + ">;");
                sb.AppendLine("\tminfilter = LINEAR;");
                sb.AppendLine("\tmagfilter = LINEAR;");
                sb.AppendLine("\tmipfilter = NONE;");
                sb.AppendLine("\tAddressU = Wrap;");
                sb.AppendLine("\tAddressV = Wrap;");
                sb.AppendLine("};");
                samplers += sb.ToString();

                sb.Remove(0, sb.ToString().Length);
                #endregion

                #region Generate Input Structures
                sb.AppendLine("struct " + stage.InputName);
                sb.AppendLine("{");
                sb.AppendLine("\tfloat2 uv : TEXCOORD" + texCoordNumber + ";");
                sb.AppendLine("};");
                stageInputStructs += sb.ToString();

                sb.Remove(0, sb.ToString().Length);

                texCoordNumber++;
                #endregion

                stageNumber++;
            }

            #region Generate VertexShaderInput
            sb.AppendLine("struct VertexShaderInput");
            sb.AppendLine("{");
            if (shader.IsSky)
            {
                sb.AppendLine("\tfloat4 position : POSITION0;");
            }
            else
            {
                sb.AppendLine("\tfloat4 position : POSITION0;");
                sb.AppendLine("\tfloat3 normal : NORMAL0;");
                sb.AppendLine("\tfloat2 texcoords : TEXCOORD0;");
                sb.AppendLine("\tfloat2 lightmapcoords : TEXCOORD1;");
                sb.AppendLine("\tfloat4 diffuse : COLOR0;");
            }
            sb.AppendLine("};");
            vertexShaderInput = sb.ToString();

            sb.Remove(0, sb.ToString().Length);
            #endregion

            #region Generate VertexShaderOutput
            sb.AppendLine("struct VertexShaderOutput");
            sb.AppendLine("{");
            sb.AppendLine("\tfloat4 position : POSITION0;");
            foreach (Q3BSPMaterialStageContent stage in shader.stages)
            {
                sb.AppendLine("\t" + stage.InputName + " " + stage.StageName + ";");
            }
            sb.AppendLine("};");
            vertexShaderOutput = sb.ToString();

            sb.Remove(0, sb.ToString().Length);
            #endregion

            #region Generate VertexShaderProgram
            sb.AppendLine("VertexShaderOutput VertexShaderProgram(VertexShaderInput input)");
            sb.AppendLine("{");
            sb.AppendLine("\tVertexShaderOutput output;");
            sb.AppendLine("\toutput.position = mul(input.position, worldViewProj);");

            if (shader.IsSky)
            {
                float zScale  = shader.SkyHeight / 128.0f;
                float zOffset = 0.707f;

                sb.AppendLine("\tfloat3 S = normalize(float3(input.position.x, input.position.z, input.position.y));");
                sb.AppendLine("\tS.z = " + zScale + " * (S.z + " + zOffset + ");");
                sb.AppendLine("\tS = normalize(S);");
                sb.AppendLine();

                foreach (Q3BSPMaterialStageContent stage in shader.stages)
                {
                    sb.AppendLine("\toutput." + stage.StageName + ".uv = float2(S.x, S.y);");
                    sb.Append(WriteTcModEffectCode(stage));
                }
            }
            else
            {
                foreach (Q3BSPMaterialStageContent stage in shader.stages)
                {
                    if (stage.IsLightmapStage)
                    {
                        sb.AppendLine("\toutput." + stage.StageName + ".uv = input.lightmapcoords;");
                    }
                    else
                    {
                        sb.AppendLine("\toutput." + stage.StageName + ".uv = input.texcoords;");
                    }

                    sb.Append(WriteTcModEffectCode(stage));
                }
            }
            sb.AppendLine();
            sb.AppendLine("\treturn output;");
            sb.AppendLine("};");

            vertexShaderProgram = sb.ToString();
            sb.Remove(0, sb.ToString().Length);
            #endregion

            #region Generate PixelShaderProgram
            sb.AppendLine("float4 PixelShaderProgram(VertexShaderOutput input) : COLOR0");
            sb.AppendLine("{");
            sb.AppendLine("\tfloat4 destination = (float4)0;");
            sb.AppendLine("\tfloat4 source = (float4)0;");
            sb.AppendLine();

            sb.Append(WriteBlendFuncEffectCode(shader));

            sb.AppendLine();
            sb.AppendLine("\treturn destination;");
            sb.AppendLine("}");

            pixelShaderProgram = sb.ToString();
            sb.Remove(0, sb.ToString().Length);
            #endregion

            #region Generate Technique
            string shaderModel = "1_1";

            if (this.shaderModel == ShaderModel.ShaderModel2 || shader.stages.Count > 4)
            {
                shaderModel = "2_0";
            }

            sb.AppendLine("technique Technique1");
            sb.AppendLine("{");
            sb.AppendLine("\tpass Pass1");
            sb.AppendLine("\t{");
            sb.AppendLine("\t\tVertexShader = compile vs_" + shaderModel + " VertexShaderProgram();");
            sb.AppendLine("\t\tPixelShader = compile ps_" + shaderModel + " PixelShaderProgram();");
            sb.AppendLine("\t}");
            sb.AppendLine("}");

            technique = sb.ToString();
            sb.Remove(0, sb.ToString().Length);
            #endregion

            if (sw != null)
            {
                sw.WriteLine("Shader: " + shader.shaderName);
                sw.Write(externs);
                sw.WriteLine();
                sw.Write(samplers);
                sw.WriteLine();
                sw.Write(stageInputStructs);
                sw.WriteLine();
                sw.Write(vertexShaderInput);
                sw.WriteLine();
                sw.Write(vertexShaderOutput);
                sw.WriteLine();
                sw.Write(vertexShaderProgram);
                sw.WriteLine();
                sw.Write(pixelShaderProgram);
                sw.WriteLine();
                sw.Write(technique);
                sw.WriteLine();
                sw.Flush();
            }

            return(externs + samplers + stageInputStructs + vertexShaderInput + vertexShaderOutput + vertexShaderProgram + pixelShaderProgram + technique);
        }
Пример #3
0
        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);
        }
Пример #4
0
        public override TOutput Process(TInput input, ContentProcessorContext context)
        {
            Dictionary <string, Q3BSPMaterialContent> processedDictionary = new Dictionary <string, Q3BSPMaterialContent>();

            #region Initialize StreamWriter, if necessary
#if DEBUG
            StreamWriter sw = new StreamWriter(context.OutputFilename.Substring(0, context.OutputFilename.LastIndexOf('.')) + ".compiled.txt");
#else
            StreamWriter sw = null;
#endif
            #endregion

            for (int i = 0; i < input.Count; i++)
            {
                Q3BSPMaterialContent shader = input[i];

                #region Throw any errors in the parsed shader
                if (shader.stages.Count > 8)
                {
                    throw new InvalidContentException(shader.shaderName + " has " + shader.stages.Count + " stages, but the maximum supported is 8.");
                }
                if (processedDictionary.ContainsKey(shader.shaderName))
                {
                    context.Logger.LogWarning("", new ContentIdentity(), "Material " + shader.shaderName + " is defined more than once.");
                    continue;
                    //throw new InvalidContentException("Material " + shader.shaderName + " is defined more than once.");
                }
                #endregion

                #region Log any needed warnings
                if (shader.stages.Count > 4 && shaderModel == ShaderModel.ShaderModel1)
                {
                    context.Logger.LogWarning("", new ContentIdentity(), shader.shaderName + " has more than 4 stages, Shader Model 2.0 is required.");
                }
                #endregion
                EffectProcessor processor = new EffectProcessor();
                processor.DebugMode = EffectProcessorDebugMode.Debug;
                EffectContent effectContent = new EffectContent();
                effectContent.EffectCode = GenerateEffectFromShader(shader, sw);
                #region Compile the Effect
#if DEBUG
                CompiledEffectContent compiledEffect = processor.Process(effectContent, context);
                //CompiledEffect compiledEffect = Effect.CompileEffectFromSource(GenerateEffectFromShader(shader, sw), null, null, CompilerOptions.Debug, TargetPlatform.Windows);
#else
                processor.DebugMode = EffectProcessorDebugMode.Auto;
                CompiledEffectContent compiledEffect = processor.Process(effectContent, context);
                //CompiledEffect compiledEffect = Effect.CompileEffectFromSource(GenerateEffectFromShader(shader, sw), null, null, CompilerOptions.None, TargetPlatform.Windows);
#endif
                #endregion

                /*
                 * if (compiledEffect.ErrorsAndWarnings.Contains("error"))
                 * {
                 *  throw new InvalidContentException(shader.shaderName + ": " + compiledEffect.ErrorsAndWarnings);
                 * }
                 */
                shader.compiledEffect = compiledEffect;
                shader.ShaderCode     = compiledEffect.GetEffectCode();
                processedDictionary.Add(shader.shaderName, shader);
            }
#if DEBUG
            sw.Flush();
            sw.Dispose();
#endif

            return(processedDictionary);
        }