/// <summary> /// Cargar shader /// </summary> public static List <QShaderData> GetFxFromText(string code, string mediaPath) { List <QShaderData> shaders = new List <QShaderData>(); QShaderTokenizer tokenizer = new QShaderTokenizer(code); // Parse a shader while (!tokenizer.EOF) { var shader = ParseShader(tokenizer); if (shader != null) { if (shader.Stages != null) { // Crea un shader de directx En base a los del formato quake shader.ShaderSrc = QShaderBuilderDX.BuildShaderSource(shader); //shader.BuildFx(); foreach (QShaderStage qStage in shader.Stages) { qStage.LoadTextures(mediaPath); } } } shaders.Add(shader); } return(shaders); }
private static QWaveForm ParseWaveform(QShaderTokenizer tokenizer) { return(new QWaveForm() { Name = tokenizer.GetNext().ToLower(), Bas = ParserTools.ToFloat(tokenizer.GetNext()), Amp = ParserTools.ToFloat(tokenizer.GetNext()), Phase = ParserTools.ToFloat(tokenizer.GetNext()), Freq = ParserTools.ToFloat(tokenizer.GetNext()) }); }
private static QShaderStage ParseStage(QShaderTokenizer tokenizer) { QShaderStage stage = new QShaderStage(); // Parse a Stage while (!tokenizer.EOF) { string token = tokenizer.GetNext(); if (token.Equals("}")) { break; } switch (token.ToLower()) { case "clampmap": stage.Clamp = true; goto case "map"; case "map": stage.Map = tokenizer.GetNext(); //.replace()/(\.jpg|\.tga)/, '.png'); break; case "animmap": stage.Map = "anim"; stage.AnimFreq = ParserTools.ToFloat(tokenizer.GetNext()); var nextMap = tokenizer.GetNext(); Match match = Regex.Match(nextMap, @"(\.jpg|\.tga)"); while (match.Success) { stage.AnimMaps.Add(nextMap); nextMap = tokenizer.GetNext(); match = Regex.Match(nextMap, @"(\.jpg|\.tga)"); } tokenizer.MovePrev(); break; case "rgbgen": stage.RgbGen = tokenizer.GetNext().ToLower(); switch (stage.RgbGen) { case "wave": stage.RgbWaveform = ParseWaveform(tokenizer); if (stage.RgbWaveform == null) { stage.RgbGen = "identity"; } break; } ; break; case "alphagen": stage.AlphaGen = tokenizer.GetNext().ToLower(); switch (stage.AlphaGen) { case "wave": stage.AlphaWaveform = ParseWaveform(tokenizer); if (stage.AlphaWaveform == null) { stage.AlphaGen = "1.0"; } break; default: break; } break; case "alphafunc": stage.AlphaFunc = tokenizer.GetNext().ToUpper(); break; case "blendfunc": stage.BlendSrc = tokenizer.GetNext(); stage.HasBlendFunc = true; if (stage.DepthWriteOverride) { stage.DepthWrite = false; } switch (stage.BlendSrc) { case "add": stage.BlendSrc = "GL_ONE"; stage.BlendDest = "GL_ONE"; break; case "blend": stage.BlendSrc = "GL_SRC_ALPHA"; stage.BlendDest = "GL_ONE_MINUS_SRC_ALPHA"; break; case "filter": stage.BlendSrc = "GL_DST_COLOR"; stage.BlendDest = "GL_ZERO"; break; default: stage.BlendDest = tokenizer.GetNext(); break; } break; case "depthfunc": stage.DepthFunc = tokenizer.GetNext().ToLower(); break; case "depthwrite": stage.DepthWrite = true; stage.DepthWriteOverride = true; break; case "tcmod": var tcMod = new QTcMod() { Type = tokenizer.GetNext().ToLower() }; switch (tcMod.Type) { case "rotate": tcMod.Angle = Geometry.DegreeToRadian(ParserTools.ToFloat(tokenizer.GetNext())); break; case "scale": tcMod.ScaleX = ParserTools.ToFloat(tokenizer.GetNext()); tcMod.ScaleY = ParserTools.ToFloat(tokenizer.GetNext()); break; case "scroll": tcMod.SSpeed = ParserTools.ToFloat(tokenizer.GetNext()); tcMod.TSpeed = ParserTools.ToFloat(tokenizer.GetNext()); break; case "stretch": tcMod.WaveForm = ParseWaveform(tokenizer); if (tcMod.WaveForm == null) { tcMod.Type = ""; } break; case "turb": tcMod.Turbulance = new QWaveForm() { Name = char.IsLetter(tokenizer.GetToken()[0]) ? tokenizer.GetNext() : "", Bas = ParserTools.ToFloat(tokenizer.GetNext()), Amp = ParserTools.ToFloat(tokenizer.GetNext()), Phase = ParserTools.ToFloat(tokenizer.GetNext()), Freq = ParserTools.ToFloat(tokenizer.GetNext()) }; break; default: tcMod.Type = ""; break; } if (!tcMod.Type.Equals("")) { stage.TcMods.Add(tcMod); } break; case "tcgen": stage.TcGen = tokenizer.GetNext(); break; default: break; } } if (stage.BlendSrc.Equals("GL_ONE") && stage.BlendDest.Equals("GL_ZERO")) { stage.HasBlendFunc = false; stage.DepthWrite = true; } return(stage); }
/// <summary> /// Parsear shader /// </summary> private static QShaderData ParseShader(QShaderTokenizer tokenizer) { QShaderData qsd = new QShaderData(); qsd.Name = tokenizer.GetNext(); string token = tokenizer.GetNext(); if (!token.Equals("{")) { return(null); } // Parse a shader while (!tokenizer.EOF) { token = tokenizer.GetNext().ToLower(); if (token.Equals("}")) { break; } switch (token) { case "{": QShaderStage stage = ParseStage(tokenizer); // I really really really don't like doing this, which basically just forces lightmaps to use the 'filter' blendmode // but if I don't a lot of textures end up looking too bright. I'm sure I'm jsut missing something, and this shouldn't // be needed. if (stage.IsLightMap() && (stage.HasBlendFunc)) { stage.BlendSrc = "GL_DST_COLOR"; stage.BlendDest = "GL_ZERO"; } // I'm having a ton of trouble getting lightingSpecular to work properly, // so this little hack gets it looking right till I can figure out the problem if (stage.AlphaGen.Equals("lightingspecular")) { stage.BlendSrc = "GL_ONE"; stage.BlendDest = "GL_ZERO"; stage.HasBlendFunc = false; stage.DepthWrite = true; } if (stage.HasBlendFunc) { qsd.Blend = true; } else { qsd.Opaque = true; } qsd.Stages.Add(stage); break; case "cull": qsd.Cull = tokenizer.GetNext(); break; case "deformvertexes": QShaderDeform deform = new QShaderDeform() { Type = tokenizer.GetNext().ToLower() }; switch (deform.Type) { case "wave": deform.Spread = 1.0f / ParserTools.ToFloat(tokenizer.GetNext()); deform.WaveForm = ParseWaveform(tokenizer); break; case "bulge": deform.BulgeWidth = ParserTools.ToFloat(tokenizer.GetNext()); deform.BulgeHeight = ParserTools.ToFloat(tokenizer.GetNext()); deform.BulgeSpeed = ParserTools.ToFloat(tokenizer.GetNext()); break; default: deform = null; break; } if (deform != null) { qsd.VertexDeforms.Add(deform); } break; case "sort": var sort = tokenizer.GetNext().ToLower(); switch (sort) { case "portal": qsd.Sort = 1; break; case "sky": qsd.Sort = 2; break; case "opaque": qsd.Sort = 3; break; case "banner": qsd.Sort = 6; break; case "underwater": qsd.Sort = 8; break; case "additive": qsd.Sort = 9; break; case "nearest": qsd.Sort = 16; break; default: qsd.Sort = int.Parse(sort); break; } ; break; case "surfaceparm": var param = tokenizer.GetNext().ToLower(); switch (param) { case "sky": qsd.Sky = true; break; default: break; } break; default: break; } } if (qsd.Sort > 0) { qsd.Sort = (qsd.Opaque ? 3 : 9); } return(qsd); }
private static QShaderStage ParseStage(QShaderTokenizer tokenizer) { QShaderStage stage = new QShaderStage(); // Parse a Stage while (!tokenizer.EOF) { string token = tokenizer.GetNext(); if (token.Equals("}")) { break; } switch (token.ToLower()) { case "clampmap": stage.Clamp = true; goto case "map"; case "map": stage.Map = tokenizer.GetNext(); //.replace()/(\.jpg|\.tga)/, '.png'); break; case "animmap": stage.Map = "anim"; stage.AnimFreq = ParserTools.ToFloat(tokenizer.GetNext()); var nextMap = tokenizer.GetNext(); Match match = Regex.Match(nextMap, @"(\.jpg|\.tga)"); while (match.Success) { stage.AnimMaps.Add(nextMap); nextMap = tokenizer.GetNext(); match = Regex.Match(nextMap, @"(\.jpg|\.tga)"); } tokenizer.MovePrev(); break; case "rgbgen": stage.RgbGen = tokenizer.GetNext().ToLower(); switch (stage.RgbGen) { case "wave": stage.RgbWaveform = ParseWaveform(tokenizer); if (stage.RgbWaveform == null) { stage.RgbGen = "identity"; } break; } ; break; case "alphagen": stage.AlphaGen = tokenizer.GetNext().ToLower(); switch (stage.AlphaGen) { case "wave": stage.AlphaWaveform = ParseWaveform(tokenizer); if (stage.AlphaWaveform == null) { stage.AlphaGen = "1.0"; } break; default: break; } break; case "alphafunc": stage.AlphaFunc = tokenizer.GetNext().ToUpper(); break; case "blendfunc": stage.BlendSrc = tokenizer.GetNext(); stage.HasBlendFunc = true; if (stage.DepthWriteOverride) { stage.DepthWrite = false; } switch (stage.BlendSrc) { case "add": stage.BlendSrc = "GL_ONE"; stage.BlendDest = "GL_ONE"; break; case "blend": stage.BlendSrc = "GL_SRC_ALPHA"; stage.BlendDest = "GL_ONE_MINUS_SRC_ALPHA"; break; case "filter": stage.BlendSrc = "GL_DST_COLOR"; stage.BlendDest = "GL_ZERO"; break; default: stage.BlendDest = tokenizer.GetNext(); break; } break; case "depthfunc": stage.DepthFunc = tokenizer.GetNext().ToLower(); break; case "depthwrite": stage.DepthWrite = true; stage.DepthWriteOverride = true; break; case "tcmod": var tcMod = new QTcMod() { Type = tokenizer.GetNext().ToLower() }; switch (tcMod.Type) { case "rotate": tcMod.Angle = Geometry.DegreeToRadian(ParserTools.ToFloat(tokenizer.GetNext())); break; case "scale": tcMod.ScaleX = ParserTools.ToFloat(tokenizer.GetNext()); tcMod.ScaleY = ParserTools.ToFloat(tokenizer.GetNext()); break; case "scroll": tcMod.SSpeed = ParserTools.ToFloat(tokenizer.GetNext()); tcMod.TSpeed = ParserTools.ToFloat(tokenizer.GetNext()); break; case "stretch": tcMod.WaveForm = ParseWaveform(tokenizer); if (tcMod.WaveForm == null) { tcMod.Type = ""; } break; case "turb": tcMod.Turbulance = new QWaveForm() { Name = char.IsLetter(tokenizer.GetToken()[0]) ? tokenizer.GetNext() : "", Bas = ParserTools.ToFloat(tokenizer.GetNext()), Amp = ParserTools.ToFloat(tokenizer.GetNext()), Phase = ParserTools.ToFloat(tokenizer.GetNext()), Freq = ParserTools.ToFloat(tokenizer.GetNext()) }; break; default: tcMod.Type = ""; break; } if (!tcMod.Type.Equals("")) { stage.TcMods.Add(tcMod); } break; case "tcgen": stage.TcGen = tokenizer.GetNext(); break; default: break; } } if (stage.BlendSrc.Equals("GL_ONE") && stage.BlendDest.Equals("GL_ZERO")) { stage.HasBlendFunc = false; stage.DepthWrite = true; } return stage; }
/// <summary> /// Parsear shader /// </summary> private static QShaderData ParseShader(QShaderTokenizer tokenizer) { QShaderData qsd = new QShaderData(); qsd.Name = tokenizer.GetNext(); string token = tokenizer.GetNext(); if (!token.Equals("{")) { return null; } // Parse a shader while(!tokenizer.EOF) { token = tokenizer.GetNext().ToLower(); if(token.Equals("}")) { break; } switch (token) { case "{": QShaderStage stage = ParseStage(tokenizer); // I really really really don't like doing this, which basically just forces lightmaps to use the 'filter' blendmode // but if I don't a lot of textures end up looking too bright. I'm sure I'm jsut missing something, and this shouldn't // be needed. if(stage.IsLightMap() && (stage.HasBlendFunc)) { stage.BlendSrc = "GL_DST_COLOR"; stage.BlendDest = "GL_ZERO"; } // I'm having a ton of trouble getting lightingSpecular to work properly, // so this little hack gets it looking right till I can figure out the problem if(stage.AlphaGen.Equals("lightingspecular")) { stage.BlendSrc = "GL_ONE"; stage.BlendDest = "GL_ZERO"; stage.HasBlendFunc = false; stage.DepthWrite = true; } if(stage.HasBlendFunc) { qsd.Blend = true; } else { qsd.Opaque = true; } qsd.Stages.Add(stage); break; case "cull": qsd.Cull = tokenizer.GetNext(); break; case "deformvertexes": QShaderDeform deform = new QShaderDeform() {Type = tokenizer.GetNext().ToLower()}; switch(deform.Type) { case "wave": deform.Spread = 1.0f / ParserTools.ToFloat(tokenizer.GetNext()); deform.WaveForm = ParseWaveform(tokenizer); break; case "bulge": deform.BulgeWidth = ParserTools.ToFloat(tokenizer.GetNext()); deform.BulgeHeight = ParserTools.ToFloat(tokenizer.GetNext()); deform.BulgeSpeed = ParserTools.ToFloat(tokenizer.GetNext()); break; default: deform = null; break; } if(deform != null) { qsd.VertexDeforms.Add(deform); } break; case "sort": var sort = tokenizer.GetNext().ToLower(); switch(sort) { case "portal": qsd.Sort = 1; break; case "sky": qsd.Sort = 2; break; case "opaque": qsd.Sort = 3; break; case "banner": qsd.Sort = 6; break; case "underwater": qsd.Sort = 8; break; case "additive": qsd.Sort = 9; break; case "nearest": qsd.Sort = 16; break; default: qsd.Sort = int.Parse(sort); break; }; break; case "surfaceparm": var param = tokenizer.GetNext().ToLower(); switch(param) { case "sky": qsd.Sky = true; break; default: break; } break; default: break; } } if (qsd.Sort > 0) { qsd.Sort = (qsd.Opaque ? 3 : 9); } return qsd; }
/// <summary> /// Cargar shader /// </summary> public static List<QShaderData> GetFxFromText(string code, string mediaPath) { List<QShaderData> shaders = new List<QShaderData>(); QShaderTokenizer tokenizer = new QShaderTokenizer(code); // Parse a shader while (!tokenizer.EOF) { var shader = ParseShader(tokenizer); if (shader != null) { if (shader.Stages != null) { // Crea un shader de directx En base a los del formato quake shader.ShaderSrc = QShaderBuilderDX.BuildShaderSource(shader); //shader.BuildFx(); foreach (QShaderStage qStage in shader.Stages) { qStage.LoadTextures(mediaPath); } } } shaders.Add(shader); } return shaders; }
private static QWaveForm ParseWaveform(QShaderTokenizer tokenizer) { return new QWaveForm() { Name = tokenizer.GetNext().ToLower(), Bas = ParserTools.ToFloat(tokenizer.GetNext()), Amp = ParserTools.ToFloat(tokenizer.GetNext()), Phase = ParserTools.ToFloat(tokenizer.GetNext()), Freq = ParserTools.ToFloat(tokenizer.GetNext()) }; }
/// <summary> /// Utilidad para detectar los recursos necesarios de mapa (texturas, shaders, etc) /// y empaquetarlos en una carpeta destino. /// Es útil para depurar todos los archivos no utilizados y crear una copia limpia /// del mapa. /// </summary> /// <param name="bspMap">Mapa ya cargado</param> /// <param name="mediaPath">Carpeta root de todas las texturas, shaders y modelos de Quake 3</param> /// <param name="targetFolder">Carpeta destino</param> public void packLevel(BspMap bspMap, string mediaPath, string targetFolder) { GuiController.Instance.Logger.log("Empaquetando: nivel: " + bspMap.Data.filePath); //copia el archivo bsp en la carpeta maps string mapa = bspMap.Data.filePath.Substring(bspMap.Data.filePath.LastIndexOf("\\") + 1); fileCopy(bspMap.Data.filePath, targetFolder + "\\maps\\" + mapa); //salva todas las texturas y los shaders textures = new TgcTexture[bspMap.Data.shaders.Length]; shaderXTextura = new QShaderData[bspMap.Data.shaders.Length]; textureFullPath = new string[bspMap.Data.shaders.Length]; for (int i = 0; i < bspMap.Data.shaders.Length; i++) { // Find the extension if any and append it to the file name string file = FindTextureExtension(bspMap.Data.shaders[i].shader, mediaPath); // Create a texture from the image if (file.Length > 0) { string shader_text = bspMap.Data.shaders[i].shader.TrimEnd(new char[] { '\0' }).Replace('/', '\\'); string ext = file.Substring(file.LastIndexOf(".")); string newTex = targetFolder + "\\" + shader_text + ext; fileCopy(file, newTex); } //Si no tiene textura entonces tiene un shader else { string shader_text = bspMap.Data.shaders[i].shader.TrimEnd(new char[] { '\0' }); if (!shader_text.Contains("/") && !bspMap.Data.shaders[i].shader.Contains("\\")) continue; string scriptDir = mediaPath + @"scripts\"; string fileScript = shader_text.Split(new char[] { '\\', '/' })[1] + ".shader"; string pathScript = scriptDir + fileScript; if (File.Exists(pathScript)) { string newShader = targetFolder + @"\scripts\" + fileScript; fileCopy(pathScript, newShader); //Reviso si el shader hace llamada a alguna textura y la guardo QShaderTokenizer tokenizer = new QShaderTokenizer(File.ReadAllText(pathScript)); while (!tokenizer.EOF) { string token = tokenizer.GetNext(); if (token.Contains(".tga") || token.Contains(".jpg")) { token = token.Replace('/', '\\'); string src = mediaPath + "\\" + token; //hay una textura que debe ser salvada if (File.Exists(src)) { fileCopy(src, targetFolder + "\\" + token); } } } } } } GuiController.Instance.Logger.log("Empaquetando Exitoso"); }
/// <summary> /// Utilidad para detectar los recursos necesarios de mapa (texturas, shaders, etc) /// y empaquetarlos en una carpeta destino. /// Es útil para depurar todos los archivos no utilizados y crear una copia limpia /// del mapa. /// </summary> /// <param name="bspMap">Mapa ya cargado</param> /// <param name="mediaPath">Carpeta root de todas las texturas, shaders y modelos de Quake 3</param> /// <param name="targetFolder">Carpeta destino</param> public void packLevel(BspMap bspMap, string mediaPath, string targetFolder) { GuiController.Instance.Logger.log("Empaquetando: nivel: " + bspMap.Data.filePath); //copia el archivo bsp en la carpeta maps string mapa = bspMap.Data.filePath.Substring(bspMap.Data.filePath.LastIndexOf("\\") + 1); fileCopy(bspMap.Data.filePath, targetFolder + "\\maps\\" + mapa); //salva todas las texturas y los shaders textures = new TgcTexture[bspMap.Data.shaders.Length]; shaderXTextura = new QShaderData[bspMap.Data.shaders.Length]; textureFullPath = new string[bspMap.Data.shaders.Length]; for (int i = 0; i < bspMap.Data.shaders.Length; i++) { // Find the extension if any and append it to the file name string file = FindTextureExtension(bspMap.Data.shaders[i].shader, mediaPath); // Create a texture from the image if (file.Length > 0) { string shader_text = bspMap.Data.shaders[i].shader.TrimEnd(new char[] { '\0' }).Replace('/', '\\'); string ext = file.Substring(file.LastIndexOf(".")); string newTex = targetFolder + "\\" + shader_text + ext; fileCopy(file, newTex); } //Si no tiene textura entonces tiene un shader else { string shader_text = bspMap.Data.shaders[i].shader.TrimEnd(new char[] { '\0' }); if (!shader_text.Contains("/") && !bspMap.Data.shaders[i].shader.Contains("\\")) { continue; } string scriptDir = mediaPath + @"scripts\"; string fileScript = shader_text.Split(new char[] { '\\', '/' })[1] + ".shader"; string pathScript = scriptDir + fileScript; if (File.Exists(pathScript)) { string newShader = targetFolder + @"\scripts\" + fileScript; fileCopy(pathScript, newShader); //Reviso si el shader hace llamada a alguna textura y la guardo QShaderTokenizer tokenizer = new QShaderTokenizer(File.ReadAllText(pathScript)); while (!tokenizer.EOF) { string token = tokenizer.GetNext(); if (token.Contains(".tga") || token.Contains(".jpg")) { token = token.Replace('/', '\\'); string src = mediaPath + "\\" + token; //hay una textura que debe ser salvada if (File.Exists(src)) { fileCopy(src, targetFolder + "\\" + token); } } } } } } GuiController.Instance.Logger.log("Empaquetando Exitoso"); }