import defined from "../Core/defined.js"; import DeveloperError from "../Core/DeveloperError.js"; /** * A function to port GLSL shaders from GLSL ES 1.00 to GLSL ES 3.00 * * This function is nowhere near comprehensive or complete. It just * handles some common cases. * * Note that this function requires the presence of the * "#define OUTPUT_DECLARATION" line that is appended * by ShaderSource. * * @private */ function modernizeShader(source, isFragmentShader) { var outputDeclarationRegex = /#define OUTPUT_DECLARATION/; var splitSource = source.split("\n"); if (/#version 300 es/g.test(source)) { return source; } var outputDeclarationLine = -1; var i, line; for (i = 0; i < splitSource.length; ++i) { line = splitSource[i]; if (outputDeclarationRegex.test(line)) { outputDeclarationLine = i; break; } } if (outputDeclarationLine === -1) { throw new DeveloperError("Could not find a #define OUTPUT_DECLARATION!"); } var outputVariables = []; for (i = 0; i < 10; i++) { var fragDataString = "gl_FragData\\[" + i + "\\]"; var newOutput = "czm_out" + i; var regex = new RegExp(fragDataString, "g"); if (regex.test(source)) { setAdd(newOutput, outputVariables); replaceInSourceString(fragDataString, newOutput, splitSource); splitSource.splice( outputDeclarationLine, 0, "layout(location = " + i + ") out vec4 " + newOutput + ";" ); outputDeclarationLine += 1; } } var czmFragColor = "czm_fragColor"; if (findInSource("gl_FragColor", splitSource)) { setAdd(czmFragColor, outputVariables); replaceInSourceString("gl_FragColor", czmFragColor, splitSource); splitSource.splice( outputDeclarationLine, 0, "layout(location = 0) out vec4 czm_fragColor;" ); outputDeclarationLine += 1; } var variableMap = getVariablePreprocessorBranch(outputVariables, splitSource); var lineAdds = {}; for (i = 0; i < splitSource.length; i++) { line = splitSource[i]; for (var variable in variableMap) { if (variableMap.hasOwnProperty(variable)) { var matchVar = new RegExp( "(layout)[^]+(out)[^]+(" + variable + ")[^]+", "g" ); if (matchVar.test(line)) { lineAdds[line] = variable; } } } } for (var layoutDeclaration in lineAdds) { if (lineAdds.hasOwnProperty(layoutDeclaration)) { var variableName = lineAdds[layoutDeclaration]; var lineNumber = splitSource.indexOf(layoutDeclaration); var entry = variableMap[variableName]; var depth = entry.length; var d; for (d = 0; d < depth; d++) { splitSource.splice(lineNumber, 0, entry[d]); } lineNumber += depth + 1; for (d = depth - 1; d >= 0; d--) { splitSource.splice(lineNumber, 0, "#endif //" + entry[d]); } } } var webgl2UniqueID = "WEBGL_2"; var webgl2DefineMacro = "#define " + webgl2UniqueID; var versionThree = "#version 300 es"; var foundVersion = false; for (i = 0; i < splitSource.length; i++) { if (/#version/.test(splitSource[i])) { splitSource[i] = versionThree; foundVersion = true; break; } } if (!foundVersion) { splitSource.splice(0, 0, versionThree); } splitSource.splice(1, 0, webgl2DefineMacro); removeExtension("EXT_draw_buffers", webgl2UniqueID, splitSource); removeExtension("EXT_frag_depth", webgl2UniqueID, splitSource); removeExtension("OES_standard_derivatives", webgl2UniqueID, splitSource); replaceInSourceString("texture2D", "texture", splitSource); replaceInSourceString("texture3D", "texture", splitSource); replaceInSourceString("textureCube", "texture", splitSource); replaceInSourceString("gl_FragDepthEXT", "gl_FragDepth", splitSource); if (isFragmentShader) { replaceInSourceString("varying", "in", splitSource); } else { replaceInSourceString("attribute", "in", splitSource); replaceInSourceString("varying", "out", splitSource); } return compileSource(splitSource); } // Note that this fails if your string looks like // searchString[singleCharacter]searchString function replaceInSourceString(str, replacement, splitSource) { var regexStr = "(^|[^\\w])(" + str + ")($|[^\\w])"; var regex = new RegExp(regexStr, "g"); var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { var line = splitSource[i]; splitSource[i] = line.replace(regex, "$1" + replacement + "$3"); } } function replaceInSourceRegex(regex, replacement, splitSource) { var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { var line = splitSource[i]; splitSource[i] = line.replace(regex, replacement); } } function findInSource(str, splitSource) { var regexStr = "(^|[^\\w])(" + str + ")($|[^\\w])"; var regex = new RegExp(regexStr, "g"); var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { var line = splitSource[i]; if (regex.test(line)) { return true; } } return false; } function compileSource(splitSource) { var wholeSource = ""; var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { wholeSource += splitSource[i] + "\n"; } return wholeSource; } function setAdd(variable, set) { if (set.indexOf(variable) === -1) { set.push(variable); } } function getVariablePreprocessorBranch(layoutVariables, splitSource) { var variableMap = {}; var numLayoutVariables = layoutVariables.length; var stack = []; for (var i = 0; i < splitSource.length; ++i) { var line = splitSource[i]; var hasIF = /(#ifdef|#if)/g.test(line); var hasELSE = /#else/g.test(line); var hasENDIF = /#endif/g.test(line); if (hasIF) { stack.push(line); } else if (hasELSE) { var top = stack[stack.length - 1]; var op = top.replace("ifdef", "ifndef"); if (/if/g.test(op)) { op = op.replace(/(#if\s+)(\S*)([^]*)/, "$1!($2)$3"); } stack.pop(); stack.push(op); } else if (hasENDIF) { stack.pop(); } else if (!/layout/g.test(line)) { for (var varIndex = 0; varIndex < numLayoutVariables; ++varIndex) { var varName = layoutVariables[varIndex]; if (line.indexOf(varName) !== -1) { if (!defined(variableMap[varName])) { variableMap[varName] = stack.slice(); } else { variableMap[varName] = variableMap[varName].filter(function (x) { return stack.indexOf(x) >= 0; }); } } } } } return variableMap; } function removeExtension(name, webgl2UniqueID, splitSource) { var regex = "#extension\\s+GL_" + name + "\\s+:\\s+[a-zA-Z0-9]+\\s*$"; replaceInSourceRegex(new RegExp(regex, "g"), "", splitSource); // replace any possible directive #ifdef (GL_EXT_extension) with WEBGL_2 unique directive replaceInSourceString("GL_" + name, webgl2UniqueID, splitSource); } export default modernizeShader;