// // WebGL Shader creation // // This whole section is a bit ugly, but it gets the job done. The job, in this case, is translating // Quake 3 shaders into GLSL shader programs. We should probably be doing a bit more normalization here. public static shader_src_t buildShaderSource(shader_t shader, stage_t stage) { shader_src_t src = new shader_src_t(); src.vertex = ShaderParser.buildVertexShader(shader, stage); src.fragment = ShaderParser.buildFragmentShader(shader, stage); return(src); }
public static shader_t parseShader(String name, ShaderTokenizer tokens) { string param; String brace = tokens.next(); if (brace != "{") { return(null); } shader_t shader = new shader_t(); shader.name = name; // Parse a shader while (!tokens.EOF()) { String token = tokens.next().ToLower(); if (token == "}") { break; } switch (token) { case "{": { stage_t stage = ShaderParser.parseStage(tokens); // 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 == true && (stage.hasBlendFunc == true)) { 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 == "lightingspecular") { stage.blendSrc = "GL_ONE"; stage.blendDest = "GL_ZERO"; stage.hasBlendFunc = false; stage.depthWrite = true; shader.stages = new List <stage_t>(); } if (stage.hasBlendFunc == true) { shader.blend = true; } else { shader.opaque = true; } shader.stages.Add(stage); } break; case "cull": shader.cull = tokens.next(); break; case "deformvertexes": deform_t deform = new deform_t(); deform.type = tokens.next().ToLower(); switch (deform.type) { case "wave": deform.spread = 1.0 / double.Parse(tokens.next()); deform.waveform = ShaderParser.parseWaveform(tokens); break; default: deform = null; break; } if (deform != null) { shader.vertexDeforms.Add(deform); } break; case "sort": String sort = tokens.next().ToLower(); switch (sort) { case "portal": shader.sort = 1; break; case "sky": shader.sort = 2; break; case "opaque": shader.sort = 3; break; case "banner": shader.sort = 6; break; case "underwater": shader.sort = 8; break; case "additive": shader.sort = 9; break; case "nearest": shader.sort = 16; break; default: shader.sort = int.Parse(sort); break; } ; break; case "surfaceparm": param = tokens.next().ToLower(); switch (param) { case "sky": shader.sky = true; break; default: break; } break; case "skyparms": param = tokens.next().ToLower(); shader.sky_env_map = new sky_env_map_p(param);; break; default: break; } } if (shader.sort < 0 || shader.sort == 0) { shader.sort = (shader.opaque ? 3 : 9); } //if(!shader['sort']) { //shader['sort'] = (shader['opaque'] ? 3 : 9); //} return(shader); }
public static fragment_shader_src_t buildFragmentShader(shader_t stageShader, stage_t stage) { ShaderBuilder shader = new ShaderBuilder(); shader.addVaryings("vTexCoord", "vec2"); shader.addVaryings("vColor", "vec4"); shader.addUniforms("texture", "sampler2D"); shader.addUniforms("time", "float"); shader.addLines("vec4 texColor = texture2D(texture, vTexCoord.st);"); switch (stage.rgbGen) { case "vertex": shader.addLines("vec3 rgb = texColor.rgb * vColor.rgb;"); break; case "wave": shader.addWaveform("rgbWave", stage.rgbWaveform, null); shader.addLines("vec3 rgb = texColor.rgb * rgbWave;"); break; default: shader.addLines("vec3 rgb = texColor.rgb;"); break; } switch (stage.alphaGen) { case "wave": shader.addWaveform("alpha", stage.alphaWaveform, null); break; case "lightingspecular": // For now this is VERY special cased. May not work well with all instances of lightingSpecular shader.addUniforms("lightmap", "sampler2D"); shader.addVaryings("vLightCoord", "vec2"); shader.addVaryings("vLight", "float"); shader.addLines( "vec4 light = texture2D(lightmap, vLightCoord.st);", "rgb *= light.rgb;", "rgb += light.rgb * texColor.a * 0.6;", // This was giving me problems, so I'm ignorning an actual specular calculation for now "float alpha = 1.0;" ); break; default: shader.addLines("float alpha = texColor.a;"); break; } if (stage.alphaFunc != null) { switch (stage.alphaFunc) { case "GT0": shader.addLines("if(alpha == 0.0) { discard; }"); break; case "LT128": shader.addLines("if(alpha >= 0.5) { discard; }"); break; case "GE128": shader.addLines("if(alpha < 0.5) { discard; }"); break; default: break; } } shader.addLines("gl_FragColor = vec4(rgb, alpha);"); fragment_shader_src_t frag_shader = new fragment_shader_src_t(); frag_shader.source_code = shader.getSource(); return(frag_shader); }
public static vertex_shader_src_t buildVertexShader(shader_t stageShader, stage_t stage) { ShaderBuilder shader; Dictionary <string, string> attribs, varyings, uniforms; List <string> statements; shader = new ShaderBuilder(); attribs = new Dictionary <string, string>(); varyings = new Dictionary <string, string>(); uniforms = new Dictionary <string, string>(); statements = new List <string>(); shader.addAttribs("position", "vec3"); shader.addAttribs("normal", "vec3"); shader.addAttribs("color", "vec4"); shader.addVaryings("vTexCoord", "vec2"); shader.addVaryings("vColor", "vec4"); shader.addUniforms("modelViewMat", "mat4"); shader.addUniforms("projectionMat", "mat4"); shader.addUniforms("time", "float"); if (stage.isLightmap == true) { shader.addAttribs("lightCoord", "vec2"); } else { shader.addAttribs("texCoord", "vec2"); } shader.addLines("vec3 defPosition = position;"); //shader.addAttribs(attribs); //shader.addVaryings(varyings); //shader.addUniforms(uniforms); //shader.addLines(statements); for (int i = 0; i < stageShader.vertexDeforms.Count; ++i) { deform_t deform = stageShader.vertexDeforms[i]; switch (deform.type) { case "wave": String name = "deform" + i.ToString(); String offName = "deformOff" + i.ToString(); shader.addLine("float " + offName + " = (position.x + position.y + position.z) * " + ShaderBuilder.toStringAsFixed(deform.spread, 4) + ";"); var phase = deform.waveform.phase; deform.waveform.phase = ShaderBuilder.toStringAsFixed((double)phase, 4) + " + " + offName; shader.addWaveform(name, deform.waveform, null); deform.waveform.phase = phase; shader.addLine("defPosition += normal * " + name + ";"); break; default: break; } } shader.addLine("vec4 worldPosition = modelViewMat * vec4(defPosition, 1.0);"); shader.addLine("vColor = color;"); if (stage.tcGen == "environment") { shader.addLines( "vec3 viewer = normalize(-worldPosition.xyz);", "float d = dot(normal, viewer);", "vec3 reflected = normal*2.0*d - viewer;", "vTexCoord = vec2(0.5, 0.5) + reflected.xy * 0.5;" ); } else { // Standard texturing if (stage.isLightmap == true) { shader.addLines("vTexCoord = lightCoord;"); } else { shader.addLines("vTexCoord = texCoord;"); } } // tcMods for (int i = 0; i < stage.tcMods.Count; ++i) { tcMod_t tcMod = stage.tcMods[i]; switch (tcMod.type) { case "rotate": shader.addLines( string.Format("float r = {0} * time;", ShaderBuilder.toStringAsFixed(tcMod.angle, 4)), "vTexCoord -= vec2(0.5, 0.5);", "vTexCoord = vec2(vTexCoord.s * cos(r) - vTexCoord.t * sin(r), vTexCoord.t * cos(r) + vTexCoord.s * sin(r));", "vTexCoord += vec2(0.5, 0.5);" ); break; case "scroll": double sSpeed = tcMod.sSpeed; // was tcMod['sSpeed'].toFixed(4) double tSpeed = tcMod.tSpeed; shader.addLines(string.Format("vTexCoord += vec2({0} * time, {1} * time); ", ShaderBuilder.toStringAsFixed(sSpeed, 4), ShaderBuilder.toStringAsFixed(tSpeed, 4))); break; case "scale": double scaleX = tcMod.scaleX; double scaleY = tcMod.scaleY; shader.addLines("vTexCoord *= vec2(" + ShaderBuilder.toStringAsFixed(scaleX, 4) + ", " + ShaderBuilder.toStringAsFixed(scaleY, 4) + ");"); break; case "stretch": shader.addWaveform("stretchWave", tcMod.waveform, null); shader.addLines( "stretchWave = 1.0 / stretchWave;", "vTexCoord *= stretchWave;", "vTexCoord += vec2(0.5 - (0.5 * stretchWave), 0.5 - (0.5 * stretchWave));" ); break; case "turb": String tName = "turbTime" + i.ToString(); shader.addLines( "float " + tName + " = " + ShaderBuilder.toStringAsFixed(tcMod.turbulance.phase, 4) + " + time * " + ShaderBuilder.toStringAsFixed(tcMod.turbulance.freq, 4) + ";", "vTexCoord.s += sin( ( ( position.x + position.z )* 1.0/128.0 * 0.125 + " + tName + " ) * 6.283) * " + ShaderBuilder.toStringAsFixed(tcMod.turbulance.amp, 4) + ';', "vTexCoord.t += sin( ( position.y * 1.0/128.0 * 0.125 + " + tName + " ) * 6.283) * " + ShaderBuilder.toStringAsFixed(tcMod.turbulance.amp, 4) + ";" ); break; default: break; } } switch (stage.alphaGen) { case "lightingspecular": shader.addAttribs("lightCoord", "vec2"); shader.addVaryings("vLightCoord", "vec2"); shader.addLines("vLightCoord = lightCoord;"); break; default: break; } shader.addLines("gl_Position = projectionMat * worldPosition;"); vertex_shader_src_t vertex_shader = new vertex_shader_src_t(); vertex_shader.source_code = shader.getSource(); return(vertex_shader); }
public static stage_t parseStage(ShaderTokenizer tokens) { stage_t stage = new stage_t(); String map; // Parse a shader while (!tokens.EOF()) { String token = tokens.next(); if (token == "}") { break; } switch (token.ToLower()) { case "clampmap": stage.clamp = true; break; case "map": map = tokens.next(); if (!Config.preserve_tga_images) { //Regex r; //r = new Regex("(\\.jpg|\\.tga)"); map = map.Replace(".tga", ".jpg"); //map = r.Replace(map, ".jpg"); //map = map.replaceAll(new RegExp(r'(\.jpg|\.tga)'), '.jpg'); } stage.map = map; //stage.map = tokens.next().replaceAll(/(\.jpg|\.tga)/, '.jpg'); break; case "animmap": stage.map = "anim"; stage.animFreq = double.Parse(tokens.next()); String nextMap = tokens.next(); while (nextMap.Contains(".tga") || nextMap.Contains(".jpg")) //while(nextMap.match(/(\.jpg|\.tga)/)) { map = nextMap; if (!Config.preserve_tga_images) { map = map.Replace(".tga", ".jpg"); //map = nextMap.replaceAll(new RegExp(r'(\.jpg|\.tga)'), '.jpg'); } // stage.animMaps.Add(map); //stage.animMaps.push(nextMap.replace(/(\.jpg|\.tga)/, '.jpg')); nextMap = tokens.next(); } tokens.prev(); break; case "rgbgen": stage.rgbGen = tokens.next().ToLower();; switch (stage.rgbGen) { case "wave": stage.rgbWaveform = ShaderParser.parseWaveform(tokens); if (stage.rgbWaveform == null) { stage.rgbGen = "identity"; } break; } ; break; case "alphagen": stage.alphaGen = tokens.next().ToLower(); switch (stage.alphaGen) { case "wave": stage.alphaWaveform = ShaderParser.parseWaveform(tokens); if (stage.alphaWaveform == null) { stage.alphaGen = "1.0"; } break; default: break; } ; break; case "alphafunc": stage.alphaFunc = tokens.next().ToUpper(); break; case "blendfunc": stage.blendSrc = tokens.next(); stage.hasBlendFunc = true; if (stage.depthWriteOverride == false) { 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 = tokens.next(); break; } break; case "depthfunc": stage.depthFunc = tokens.next().ToLower(); break; case "depthwrite": stage.depthWrite = true; stage.depthWriteOverride = true; break; case "tcmod": tcMod_t tcMod = new tcMod_t(); tcMod.type = tokens.next().ToLower(); switch (tcMod.type) { case "rotate": tcMod.angle = double.Parse(tokens.next()) * (Math.PI / 180); break; case "scale": tcMod.scaleX = double.Parse(tokens.next()); tcMod.scaleY = double.Parse(tokens.next()); break; case "scroll": tcMod.sSpeed = double.Parse(tokens.next()); tcMod.tSpeed = double.Parse(tokens.next()); break; case "stretch": tcMod.waveform = ShaderParser.parseWaveform(tokens); if (tcMod.waveform == null) { tcMod.type = null; } break; case "turb": tcMod.turbulance = new turbulance_t(); tcMod.turbulance.@base = double.Parse(tokens.next()); tcMod.turbulance.amp = double.Parse(tokens.next()); tcMod.turbulance.phase = double.Parse(tokens.next()); tcMod.turbulance.freq = double.Parse(tokens.next()); break; default: tcMod.type = null; break; } if (tcMod.type != null) { stage.tcMods.Add(tcMod); } break; case "tcgen": stage.tcGen = tokens.next(); break; default: break; } } if (stage.blendSrc == "GL_ONE" && stage.blendDest == "GL_ZERO") { stage.hasBlendFunc = false; stage.depthWrite = true; } stage.isLightmap = stage.map == "\\$lightmap"; return(stage); }