| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 | import defaultValue from "../Core/defaultValue.js";import defined from "../Core/defined.js";import DeveloperError from "../Core/DeveloperError.js";import modernizeShader from "../Renderer/modernizeShader.js";import CzmBuiltins from "../Shaders/Builtin/CzmBuiltins.js";import AutomaticUniforms from "./AutomaticUniforms.js";function removeComments(source) {  // remove inline comments  source = source.replace(/\/\/.*/g, "");  // remove multiline comment block  return source.replace(/\/\*\*[\s\S]*?\*\//gm, function (match) {    // preserve the number of lines in the comment block so the line numbers will be correct when debugging shaders    const numberOfLines = match.match(/\n/gm).length;    let replacement = "";    for (let lineNumber = 0; lineNumber < numberOfLines; ++lineNumber) {      replacement += "\n";    }    return replacement;  });}function getDependencyNode(name, glslSource, nodes) {  let dependencyNode;  // check if already loaded  for (let i = 0; i < nodes.length; ++i) {    if (nodes[i].name === name) {      dependencyNode = nodes[i];    }  }  if (!defined(dependencyNode)) {    // strip doc comments so we don't accidentally try to determine a dependency for something found    // in a comment    glslSource = removeComments(glslSource);    // create new node    dependencyNode = {      name: name,      glslSource: glslSource,      dependsOn: [],      requiredBy: [],      evaluated: false,    };    nodes.push(dependencyNode);  }  return dependencyNode;}function generateDependencies(currentNode, dependencyNodes) {  if (currentNode.evaluated) {    return;  }  currentNode.evaluated = true;  // identify all dependencies that are referenced from this glsl source code  let czmMatches = currentNode.glslSource.match(/\bczm_[a-zA-Z0-9_]*/g);  if (defined(czmMatches) && czmMatches !== null) {    // remove duplicates    czmMatches = czmMatches.filter(function (elem, pos) {      return czmMatches.indexOf(elem) === pos;    });    czmMatches.forEach(function (element) {      if (        element !== currentNode.name &&        ShaderSource._czmBuiltinsAndUniforms.hasOwnProperty(element)      ) {        const referencedNode = getDependencyNode(          element,          ShaderSource._czmBuiltinsAndUniforms[element],          dependencyNodes        );        currentNode.dependsOn.push(referencedNode);        referencedNode.requiredBy.push(currentNode);        // recursive call to find any dependencies of the new node        generateDependencies(referencedNode, dependencyNodes);      }    });  }}function sortDependencies(dependencyNodes) {  const nodesWithoutIncomingEdges = [];  const allNodes = [];  while (dependencyNodes.length > 0) {    const node = dependencyNodes.pop();    allNodes.push(node);    if (node.requiredBy.length === 0) {      nodesWithoutIncomingEdges.push(node);    }  }  while (nodesWithoutIncomingEdges.length > 0) {    const currentNode = nodesWithoutIncomingEdges.shift();    dependencyNodes.push(currentNode);    for (let i = 0; i < currentNode.dependsOn.length; ++i) {      // remove the edge from the graph      const referencedNode = currentNode.dependsOn[i];      const index = referencedNode.requiredBy.indexOf(currentNode);      referencedNode.requiredBy.splice(index, 1);      // if referenced node has no more incoming edges, add to list      if (referencedNode.requiredBy.length === 0) {        nodesWithoutIncomingEdges.push(referencedNode);      }    }  }  // if there are any nodes left with incoming edges, then there was a circular dependency somewhere in the graph  const badNodes = [];  for (let j = 0; j < allNodes.length; ++j) {    if (allNodes[j].requiredBy.length !== 0) {      badNodes.push(allNodes[j]);    }  }  //>>includeStart('debug', pragmas.debug);  if (badNodes.length !== 0) {    let message =      "A circular dependency was found in the following built-in functions/structs/constants: \n";    for (let k = 0; k < badNodes.length; ++k) {      message = `${message + badNodes[k].name}\n`;    }    throw new DeveloperError(message);  }  //>>includeEnd('debug');}function getBuiltinsAndAutomaticUniforms(shaderSource) {  // generate a dependency graph for builtin functions  const dependencyNodes = [];  const root = getDependencyNode("main", shaderSource, dependencyNodes);  generateDependencies(root, dependencyNodes);  sortDependencies(dependencyNodes);  // Concatenate the source code for the function dependencies.  // Iterate in reverse so that dependent items are declared before they are used.  let builtinsSource = "";  for (let i = dependencyNodes.length - 1; i >= 0; --i) {    builtinsSource = `${builtinsSource + dependencyNodes[i].glslSource}\n`;  }  return builtinsSource.replace(root.glslSource, "");}function combineShader(shaderSource, isFragmentShader, context) {  let i;  let length;  // Combine shader sources, generally for pseudo-polymorphism, e.g., czm_getMaterial.  let combinedSources = "";  const sources = shaderSource.sources;  if (defined(sources)) {    for (i = 0, length = sources.length; i < length; ++i) {      // #line needs to be on its own line.      combinedSources += `\n#line 0\n${sources[i]}`;    }  }  combinedSources = removeComments(combinedSources);  // Extract existing shader version from sources  let version;  combinedSources = combinedSources.replace(/#version\s+(.*?)\n/gm, function (    match,    group1  ) {    //>>includeStart('debug', pragmas.debug);    if (defined(version) && version !== group1) {      throw new DeveloperError(        `inconsistent versions found: ${version} and ${group1}`      );    }    //>>includeEnd('debug');    // Extract #version to put at the top    version = group1;    // Replace original #version directive with a new line so the line numbers    // are not off by one.  There can be only one #version directive    // and it must appear at the top of the source, only preceded by    // whitespace and comments.    return "\n";  });  // Extract shader extensions from sources  const extensions = [];  combinedSources = combinedSources.replace(/#extension.*\n/gm, function (    match  ) {    // Extract extension to put at the top    extensions.push(match);    // Replace original #extension directive with a new line so the line numbers    // are not off by one.    return "\n";  });  // Remove precision qualifier  combinedSources = combinedSources.replace(    /precision\s(lowp|mediump|highp)\s(float|int);/,    ""  );  // Replace main() for picked if desired.  const pickColorQualifier = shaderSource.pickColorQualifier;  if (defined(pickColorQualifier)) {    combinedSources = ShaderSource.createPickFragmentShaderSource(      combinedSources,      pickColorQualifier    );  }  // combine into single string  let result = "";  // #version must be first  // defaults to #version 100 if not specified  if (defined(version)) {    result = `#version ${version}\n`;  }  const extensionsLength = extensions.length;  for (i = 0; i < extensionsLength; i++) {    result += extensions[i];  }  if (isFragmentShader) {    // If high precision isn't support replace occurrences of highp with mediump    // The highp keyword is not always available on older mobile devices    // See https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#In_WebGL_1_highp_float_support_is_optional_in_fragment_shaders    result +=      "\#ifdef GL_FRAGMENT_PRECISION_HIGH\n\    precision highp float;\n\    precision highp int;\n\#else\n\    precision mediump float;\n\    precision mediump int;\n\    #define highp mediump\n\#endif\n\n";  }  // Prepend #defines for uber-shaders  const defines = shaderSource.defines;  if (defined(defines)) {    for (i = 0, length = defines.length; i < length; ++i) {      const define = defines[i];      if (define.length !== 0) {        result += `#define ${define}\n`;      }    }  }  // GLSLModernizer inserts its own layout qualifiers  // at this position in the source  if (context.webgl2) {    result += "#define OUTPUT_DECLARATION\n\n";  }  // Define a constant for the OES_texture_float_linear extension since WebGL does not.  if (context.textureFloatLinear) {    result += "#define OES_texture_float_linear\n\n";  }  // Define a constant for the OES_texture_float extension since WebGL does not.  if (context.floatingPointTexture) {    result += "#define OES_texture_float\n\n";  }  // append built-ins  if (shaderSource.includeBuiltIns) {    result += getBuiltinsAndAutomaticUniforms(combinedSources);  }  // reset line number  result += "\n#line 0\n";  // append actual source  result += combinedSources;  // modernize the source  if (context.webgl2) {    result = modernizeShader(result, isFragmentShader, true);  }  return result;}/** * An object containing various inputs that will be combined to form a final GLSL shader string. * * @param {Object} [options] Object with the following properties: * @param {String[]} [options.sources] An array of strings to combine containing GLSL code for the shader. * @param {String[]} [options.defines] An array of strings containing GLSL identifiers to <code>#define</code>. * @param {String} [options.pickColorQualifier] The GLSL qualifier, <code>uniform</code> or <code>varying</code>, for the input <code>czm_pickColor</code>.  When defined, a pick fragment shader is generated. * @param {Boolean} [options.includeBuiltIns=true] If true, referenced built-in functions will be included with the combined shader.  Set to false if this shader will become a source in another shader, to avoid duplicating functions. * * @exception {DeveloperError} options.pickColorQualifier must be 'uniform' or 'varying'. * * @example * // 1. Prepend #defines to a shader * const source = new Cesium.ShaderSource({ *   defines : ['WHITE'], *   sources : ['void main() { \n#ifdef WHITE\n gl_FragColor = vec4(1.0); \n#else\n gl_FragColor = vec4(0.0); \n#endif\n }'] * }); * * // 2. Modify a fragment shader for picking * const source2 = new Cesium.ShaderSource({ *   sources : ['void main() { gl_FragColor = vec4(1.0); }'], *   pickColorQualifier : 'uniform' * }); * * @private */function ShaderSource(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const pickColorQualifier = options.pickColorQualifier;  //>>includeStart('debug', pragmas.debug);  if (    defined(pickColorQualifier) &&    pickColorQualifier !== "uniform" &&    pickColorQualifier !== "varying"  ) {    throw new DeveloperError(      "options.pickColorQualifier must be 'uniform' or 'varying'."    );  }  //>>includeEnd('debug');  this.defines = defined(options.defines) ? options.defines.slice(0) : [];  this.sources = defined(options.sources) ? options.sources.slice(0) : [];  this.pickColorQualifier = pickColorQualifier;  this.includeBuiltIns = defaultValue(options.includeBuiltIns, true);}ShaderSource.prototype.clone = function () {  return new ShaderSource({    sources: this.sources,    defines: this.defines,    pickColorQualifier: this.pickColorQualifier,    includeBuiltIns: this.includeBuiltIns,  });};ShaderSource.replaceMain = function (source, renamedMain) {  renamedMain = `void ${renamedMain}()`;  return source.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, renamedMain);};/** * Create a single string containing the full, combined vertex shader with all dependencies and defines. * * @param {Context} context The current rendering context * * @returns {String} The combined shader string. */ShaderSource.prototype.createCombinedVertexShader = function (context) {  return combineShader(this, false, context);};/** * Create a single string containing the full, combined fragment shader with all dependencies and defines. * * @param {Context} context The current rendering context * * @returns {String} The combined shader string. */ShaderSource.prototype.createCombinedFragmentShader = function (context) {  return combineShader(this, true, context);};/** * For ShaderProgram testing * @private */ShaderSource._czmBuiltinsAndUniforms = {};// combine automatic uniforms and Cesium built-insfor (const builtinName in CzmBuiltins) {  if (CzmBuiltins.hasOwnProperty(builtinName)) {    ShaderSource._czmBuiltinsAndUniforms[builtinName] =      CzmBuiltins[builtinName];  }}for (const uniformName in AutomaticUniforms) {  if (AutomaticUniforms.hasOwnProperty(uniformName)) {    const uniform = AutomaticUniforms[uniformName];    if (typeof uniform.getDeclaration === "function") {      ShaderSource._czmBuiltinsAndUniforms[        uniformName      ] = uniform.getDeclaration(uniformName);    }  }}ShaderSource.createPickVertexShaderSource = function (vertexShaderSource) {  const renamedVS = ShaderSource.replaceMain(    vertexShaderSource,    "czm_old_main"  );  const pickMain =    "attribute vec4 pickColor; \n" +    "varying vec4 czm_pickColor; \n" +    "void main() \n" +    "{ \n" +    "    czm_old_main(); \n" +    "    czm_pickColor = pickColor; \n" +    "}";  return `${renamedVS}\n${pickMain}`;};ShaderSource.createPickFragmentShaderSource = function (  fragmentShaderSource,  pickColorQualifier) {  const renamedFS = ShaderSource.replaceMain(    fragmentShaderSource,    "czm_old_main"  );  const pickMain =    `${pickColorQualifier} vec4 czm_pickColor; \n` +    `void main() \n` +    `{ \n` +    `    czm_old_main(); \n` +    `    if (gl_FragColor.a == 0.0) { \n` +    `       discard; \n` +    `    } \n` +    `    gl_FragColor = czm_pickColor; \n` +    `}`;  return `${renamedFS}\n${pickMain}`;};function containsString(shaderSource, string) {  const sources = shaderSource.sources;  const sourcesLength = sources.length;  for (let i = 0; i < sourcesLength; ++i) {    if (sources[i].indexOf(string) !== -1) {      return true;    }  }  return false;}function findFirstString(shaderSource, strings) {  const stringsLength = strings.length;  for (let i = 0; i < stringsLength; ++i) {    const string = strings[i];    if (containsString(shaderSource, string)) {      return string;    }  }  return undefined;}const normalVaryingNames = ["v_normalEC", "v_normal"];ShaderSource.findNormalVarying = function (shaderSource) {  // Fix for ModelExperimental: the shader text always has the word v_normalEC  // wrapped in an #ifdef so instead of looking for v_normalEC look for the define  if (containsString(shaderSource, "#ifdef HAS_NORMALS")) {    if (containsString(shaderSource, "#define HAS_NORMALS")) {      return "v_normalEC";    }    return undefined;  }  return findFirstString(shaderSource, normalVaryingNames);};const positionVaryingNames = ["v_positionEC"];ShaderSource.findPositionVarying = function (shaderSource) {  return findFirstString(shaderSource, positionVaryingNames);};export default ShaderSource;
 |