123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- 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) {
- const outputDeclarationRegex = /#define OUTPUT_DECLARATION/;
- const splitSource = source.split("\n");
- if (/#version 300 es/g.test(source)) {
- return source;
- }
- let outputDeclarationLine = -1;
- let 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!");
- }
- const outputVariables = [];
- for (i = 0; i < 10; i++) {
- const fragDataString = `gl_FragData\\[${i}\\]`;
- const newOutput = `czm_out${i}`;
- const 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;
- }
- }
- const 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;
- }
- const variableMap = getVariablePreprocessorBranch(
- outputVariables,
- splitSource
- );
- const lineAdds = {};
- for (i = 0; i < splitSource.length; i++) {
- line = splitSource[i];
- for (const variable in variableMap) {
- if (variableMap.hasOwnProperty(variable)) {
- const matchVar = new RegExp(
- `(layout)[^]+(out)[^]+(${variable})[^]+`,
- "g"
- );
- if (matchVar.test(line)) {
- lineAdds[line] = variable;
- }
- }
- }
- }
- for (const layoutDeclaration in lineAdds) {
- if (lineAdds.hasOwnProperty(layoutDeclaration)) {
- const variableName = lineAdds[layoutDeclaration];
- let lineNumber = splitSource.indexOf(layoutDeclaration);
- const entry = variableMap[variableName];
- const depth = entry.length;
- for (let d = 0; d < depth; d++) {
- splitSource.splice(lineNumber, 0, entry[d]);
- }
- lineNumber += depth + 1;
- for (let d = depth - 1; d >= 0; d--) {
- splitSource.splice(lineNumber, 0, `#endif //${entry[d]}`);
- }
- }
- }
- const webgl2UniqueID = "WEBGL_2";
- const webgl2DefineMacro = `#define ${webgl2UniqueID}`;
- const versionThree = "#version 300 es";
- let 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) {
- const regexStr = `(^|[^\\w])(${str})($|[^\\w])`;
- const regex = new RegExp(regexStr, "g");
- const splitSourceLength = splitSource.length;
- for (let i = 0; i < splitSourceLength; ++i) {
- const line = splitSource[i];
- splitSource[i] = line.replace(regex, `$1${replacement}$3`);
- }
- }
- function replaceInSourceRegex(regex, replacement, splitSource) {
- const splitSourceLength = splitSource.length;
- for (let i = 0; i < splitSourceLength; ++i) {
- const line = splitSource[i];
- splitSource[i] = line.replace(regex, replacement);
- }
- }
- function findInSource(str, splitSource) {
- const regexStr = `(^|[^\\w])(${str})($|[^\\w])`;
- const regex = new RegExp(regexStr, "g");
- const splitSourceLength = splitSource.length;
- for (let i = 0; i < splitSourceLength; ++i) {
- const line = splitSource[i];
- if (regex.test(line)) {
- return true;
- }
- }
- return false;
- }
- function compileSource(splitSource) {
- let wholeSource = "";
- const splitSourceLength = splitSource.length;
- for (let 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) {
- const variableMap = {};
- const numLayoutVariables = layoutVariables.length;
- const stack = [];
- for (let i = 0; i < splitSource.length; ++i) {
- const line = splitSource[i];
- const hasIF = /(#ifdef|#if)/g.test(line);
- const hasELSE = /#else/g.test(line);
- const hasENDIF = /#endif/g.test(line);
- if (hasIF) {
- stack.push(line);
- } else if (hasELSE) {
- const top = stack[stack.length - 1];
- let 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 (let varIndex = 0; varIndex < numLayoutVariables; ++varIndex) {
- const 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) {
- const 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;
|