(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports", "../../nodes/core/NodeBuilder.js", "./SlotNode.js", "../../nodes/parsers/GLSLNodeParser.js", "./WebGLPhysicalContextNode.js", "three"], factory);
  } else if (typeof exports !== "undefined") {
    factory(exports, require("../../nodes/core/NodeBuilder.js"), require("./SlotNode.js"), require("../../nodes/parsers/GLSLNodeParser.js"), require("./WebGLPhysicalContextNode.js"), require("three"));
  } else {
    var mod = {
      exports: {}
    };
    factory(mod.exports, global.NodeBuilder, global.SlotNode, global.GLSLNodeParser, global.WebGLPhysicalContextNode, global.three);
    global.WebGLNodeBuilder = mod.exports;
  }
})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _NodeBuilder2, _SlotNode, _GLSLNodeParser, _WebGLPhysicalContextNode, _three) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.WebGLNodeBuilder = void 0;
  _NodeBuilder2 = _interopRequireDefault(_NodeBuilder2);
  _SlotNode = _interopRequireDefault(_SlotNode);
  _GLSLNodeParser = _interopRequireDefault(_GLSLNodeParser);
  _WebGLPhysicalContextNode = _interopRequireDefault(_WebGLPhysicalContextNode);

  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }

  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

  function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }

  function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); }

  function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }

  function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }

  function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

  function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }

  function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }

  function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

  var shaderStages = ['vertex', 'fragment'];

  function getIncludeSnippet(name) {
    return "#include <".concat(name, ">");
  }

  function getShaderStageProperty(shaderStage) {
    return "".concat(shaderStage, "Shader");
  }

  var WebGLNodeBuilder = /*#__PURE__*/function (_NodeBuilder) {
    _inherits(WebGLNodeBuilder, _NodeBuilder);

    var _super = _createSuper(WebGLNodeBuilder);

    function WebGLNodeBuilder(object, renderer, shader) {
      var _this;

      _classCallCheck(this, WebGLNodeBuilder);

      _this = _super.call(this, object, renderer, new _GLSLNodeParser.default());
      _this.shader = shader;
      _this.slots = {
        vertex: [],
        fragment: []
      };

      _this._parseObject();

      return _this;
    }

    _createClass(WebGLNodeBuilder, [{
      key: "addSlot",
      value: function addSlot(shaderStage, slotNode) {
        this.slots[shaderStage].push(slotNode);
        return this.addFlow(shaderStage, slotNode);
      }
    }, {
      key: "addFlowCode",
      value: function addFlowCode(code) {
        if (!/;\s*$/.test(code)) {
          code += ';';
        }

        _get(_getPrototypeOf(WebGLNodeBuilder.prototype), "addFlowCode", this).call(this, code + '\n\t');
      }
    }, {
      key: "_parseObject",
      value: function _parseObject() {
        var material = this.material; // parse inputs

        if (material.colorNode && material.colorNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.colorNode, 'COLOR', 'vec4'));
        }

        if (material.opacityNode && material.opacityNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.opacityNode, 'OPACITY', 'float'));
        }

        if (material.normalNode && material.normalNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.normalNode, 'NORMAL', 'vec3'));
        }

        if (material.emissiveNode && material.emissiveNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.emissiveNode, 'EMISSIVE', 'vec3'));
        }

        if (material.metalnessNode && material.metalnessNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.metalnessNode, 'METALNESS', 'float'));
        }

        if (material.roughnessNode && material.roughnessNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.roughnessNode, 'ROUGHNESS', 'float'));
        }

        if (material.clearcoatNode && material.clearcoatNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.clearcoatNode, 'CLEARCOAT', 'float'));
        }

        if (material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode) {
          this.addSlot('fragment', new _SlotNode.default(material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float'));
        }

        if (material.envNode && material.envNode.isNode) {
          var envRadianceNode = new _WebGLPhysicalContextNode.default(_WebGLPhysicalContextNode.default.RADIANCE, material.envNode);
          var envIrradianceNode = new _WebGLPhysicalContextNode.default(_WebGLPhysicalContextNode.default.IRRADIANCE, material.envNode);
          this.addSlot('fragment', new _SlotNode.default(envRadianceNode, 'RADIANCE', 'vec3'));
          this.addSlot('fragment', new _SlotNode.default(envIrradianceNode, 'IRRADIANCE', 'vec3'));
        }

        if (material.sizeNode && material.sizeNode.isNode) {
          this.addSlot('vertex', new _SlotNode.default(material.sizeNode, 'SIZE', 'float'));
        }

        if (material.positionNode && material.positionNode.isNode) {
          this.addSlot('vertex', new _SlotNode.default(material.positionNode, 'POSITION', 'vec3'));
        }
      }
    }, {
      key: "getTexture",
      value: function getTexture(textureProperty, uvSnippet) {
        var biasSnippet = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

        if (biasSnippet !== null) {
          return "texture2D( ".concat(textureProperty, ", ").concat(uvSnippet, ", ").concat(biasSnippet, " )");
        } else {
          return "texture2D( ".concat(textureProperty, ", ").concat(uvSnippet, " )");
        }
      }
    }, {
      key: "getCubeTexture",
      value: function getCubeTexture(textureProperty, uvSnippet) {
        var biasSnippet = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
        var textureCube = 'textureCubeLodEXT'; // textureCubeLodEXT textureLod

        if (biasSnippet !== null) {
          return "".concat(textureCube, "( ").concat(textureProperty, ", ").concat(uvSnippet, ", ").concat(biasSnippet, " )");
        } else {
          return "".concat(textureCube, "( ").concat(textureProperty, ", ").concat(uvSnippet, " )");
        }
      }
    }, {
      key: "getUniforms",
      value: function getUniforms(shaderStage) {
        var uniforms = this.uniforms[shaderStage];
        var snippet = '';

        var _iterator = _createForOfIteratorHelper(uniforms),
            _step;

        try {
          for (_iterator.s(); !(_step = _iterator.n()).done;) {
            var uniform = _step.value;

            if (uniform.type === 'texture') {
              snippet += "uniform sampler2D ".concat(uniform.name, "; ");
            } else if (uniform.type === 'cubeTexture') {
              snippet += "uniform samplerCube ".concat(uniform.name, "; ");
            } else {
              var vectorType = this.getVectorType(uniform.type);
              snippet += "uniform ".concat(vectorType, " ").concat(uniform.name, "; ");
            }
          }
        } catch (err) {
          _iterator.e(err);
        } finally {
          _iterator.f();
        }

        return snippet;
      }
    }, {
      key: "getAttributes",
      value: function getAttributes(shaderStage) {
        var snippet = '';

        if (shaderStage === 'vertex') {
          var attributes = this.attributes;

          for (var index = 0; index < attributes.length; index++) {
            var attribute = attributes[index]; // ignore common attributes to prevent redefinitions

            if (attribute.name === 'uv' || attribute.name === 'position' || attribute.name === 'normal') continue;
            snippet += "attribute ".concat(attribute.type, " ").concat(attribute.name, "; ");
          }
        }

        return snippet;
      }
    }, {
      key: "getVarys",
      value: function getVarys(shaderStage) {
        var snippet = '';
        var varys = this.varys;

        for (var index = 0; index < varys.length; index++) {
          var vary = varys[index];
          snippet += "varying ".concat(vary.type, " ").concat(vary.name, "; ");
        }

        return snippet;
      }
    }, {
      key: "addCodeAfterSnippet",
      value: function addCodeAfterSnippet(shaderStage, snippet, code) {
        var shaderProperty = getShaderStageProperty(shaderStage);
        var source = this[shaderProperty];
        var index = source.indexOf(snippet);

        if (index !== -1) {
          var start = source.substring(0, index + snippet.length);
          var end = source.substring(index + snippet.length);
          source = "".concat(start, "\n").concat(code, "\n").concat(end);
        }

        this[shaderProperty] = source;
      }
    }, {
      key: "addCodeAfterInclude",
      value: function addCodeAfterInclude(shaderStage, includeName, code) {
        var includeSnippet = getIncludeSnippet(includeName);
        this.addCodeAfterSnippet(shaderStage, includeSnippet, code);
      }
    }, {
      key: "replaceCode",
      value: function replaceCode(shaderStage, source, target) {
        var shaderProperty = getShaderStageProperty(shaderStage);
        this.shader[shaderProperty] = this.shader[shaderProperty].replaceAll(source, target);
      }
    }, {
      key: "parseInclude",
      value: function parseInclude(shaderStage) {
        for (var _len = arguments.length, includes = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
          includes[_key - 1] = arguments[_key];
        }

        for (var _i = 0, _includes = includes; _i < _includes.length; _i++) {
          var name = _includes[_i];
          var includeSnippet = getIncludeSnippet(name);
          var code = _three.ShaderChunk[name];
          this.replaceCode(shaderStage, includeSnippet, code);
        }
      }
    }, {
      key: "getTextureEncodingFromMap",
      value: function getTextureEncodingFromMap(map) {
        /*
        		const isWebGL2 = this.renderer.capabilities.isWebGL2;
        
        		if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) {
        
        			return LinearEncoding; // disable inline decode for sRGB textures in WebGL 2
        
        		}
        */
        return _get(_getPrototypeOf(WebGLNodeBuilder.prototype), "getTextureEncodingFromMap", this).call(this, map);
      }
    }, {
      key: "buildCode",
      value: function buildCode() {
        var shaderData = {};

        var _iterator2 = _createForOfIteratorHelper(shaderStages),
            _step2;

        try {
          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
            var shaderStage = _step2.value;
            var uniforms = this.getUniforms(shaderStage);
            var attributes = this.getAttributes(shaderStage);
            var varys = this.getVarys(shaderStage);
            var vars = this.getVars(shaderStage);
            var codes = this.getCodes(shaderStage);
            shaderData[shaderStage] = "".concat(this.getSignature(), "\n// <node_builder>\n\n// uniforms\n").concat(uniforms, "\n\n// attributes\n").concat(attributes, "\n\n// varys\n").concat(varys, "\n\n// vars\n").concat(vars, "\n\n// codes\n").concat(codes, "\n\n// </node_builder>\n\n").concat(this.shader[getShaderStageProperty(shaderStage)], "\n");
          }
        } catch (err) {
          _iterator2.e(err);
        } finally {
          _iterator2.f();
        }

        this.vertexShader = shaderData.vertex;
        this.fragmentShader = shaderData.fragment;
      }
    }, {
      key: "build",
      value: function build() {
        _get(_getPrototypeOf(WebGLNodeBuilder.prototype), "build", this).call(this);

        this._addSnippets();

        this._addUniforms();

        this.shader.vertexShader = this.vertexShader;
        this.shader.fragmentShader = this.fragmentShader;
        return this;
      }
    }, {
      key: "getSlot",
      value: function getSlot(shaderStage, name) {
        var slots = this.slots[shaderStage];

        var _iterator3 = _createForOfIteratorHelper(slots),
            _step3;

        try {
          for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
            var node = _step3.value;

            if (node.name === name) {
              return this.getFlowData(shaderStage, node);
            }
          }
        } catch (err) {
          _iterator3.e(err);
        } finally {
          _iterator3.f();
        }
      }
    }, {
      key: "_addSnippets",
      value: function _addSnippets() {
        this.parseInclude('fragment', 'lights_physical_fragment');
        var colorSlot = this.getSlot('fragment', 'COLOR');
        var normalSlot = this.getSlot('fragment', 'NORMAL');
        var opacityNode = this.getSlot('fragment', 'OPACITY');
        var emissiveNode = this.getSlot('fragment', 'EMISSIVE');
        var roughnessNode = this.getSlot('fragment', 'ROUGHNESS');
        var metalnessNode = this.getSlot('fragment', 'METALNESS');
        var clearcoatNode = this.getSlot('fragment', 'CLEARCOAT');
        var clearcoatRoughnessNode = this.getSlot('fragment', 'CLEARCOAT_ROUGHNESS');
        var positionNode = this.getSlot('vertex', 'POSITION');
        var sizeNode = this.getSlot('vertex', 'SIZE');

        if (colorSlot !== undefined) {
          this.addCodeAfterInclude('fragment', 'color_fragment', "".concat(colorSlot.code, "\n\tdiffuseColor = ").concat(colorSlot.result, ";"));
        }

        if (normalSlot !== undefined) {
          this.addCodeAfterInclude('fragment', 'normal_fragment_begin', "".concat(normalSlot.code, "\n\tnormal = ").concat(normalSlot.result, ";"));
        }

        if (opacityNode !== undefined) {
          this.addCodeAfterInclude('fragment', 'alphamap_fragment', "".concat(opacityNode.code, "\n\tdiffuseColor.a = ").concat(opacityNode.result, ";"));
        }

        if (emissiveNode !== undefined) {
          this.addCodeAfterInclude('fragment', 'emissivemap_fragment', "".concat(emissiveNode.code, "\n\ttotalEmissiveRadiance = ").concat(emissiveNode.result, ";"));
        }

        if (roughnessNode !== undefined) {
          this.addCodeAfterInclude('fragment', 'roughnessmap_fragment', "".concat(roughnessNode.code, "\n\troughnessFactor = ").concat(roughnessNode.result, ";"));
        }

        if (metalnessNode !== undefined) {
          this.addCodeAfterInclude('fragment', 'metalnessmap_fragment', "".concat(metalnessNode.code, "\n\tmetalnessFactor = ").concat(metalnessNode.result, ";"));
        }

        if (clearcoatNode !== undefined) {
          this.addCodeAfterSnippet('fragment', 'material.clearcoatRoughness = clearcoatRoughness;', "".concat(clearcoatNode.code, "\n\tmaterial.clearcoat = ").concat(clearcoatNode.result, ";"));
        }

        if (clearcoatRoughnessNode !== undefined) {
          this.addCodeAfterSnippet('fragment', 'material.clearcoatRoughness = clearcoatRoughness;', "".concat(clearcoatRoughnessNode.code, "\n\tmaterial.clearcoatRoughness = ").concat(clearcoatRoughnessNode.result, ";"));
        }

        if (positionNode !== undefined) {
          this.addCodeAfterInclude('vertex', 'begin_vertex', "".concat(positionNode.code, "\n\ttransformed = ").concat(positionNode.result, ";"));
        }

        if (sizeNode !== undefined) {
          this.addCodeAfterSnippet('vertex', 'gl_PointSize = size;', "".concat(sizeNode.code, "\n\tgl_PointSize = ").concat(sizeNode.result, ";"));
        }

        var _iterator4 = _createForOfIteratorHelper(shaderStages),
            _step4;

        try {
          for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
            var shaderStage = _step4.value;
            this.addCodeAfterSnippet(shaderStage, 'main() {', this.flowCode[shaderStage]);
          }
        } catch (err) {
          _iterator4.e(err);
        } finally {
          _iterator4.f();
        }
      }
    }, {
      key: "_addUniforms",
      value: function _addUniforms() {
        var _iterator5 = _createForOfIteratorHelper(shaderStages),
            _step5;

        try {
          for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
            var shaderStage = _step5.value;

            // uniforms
            var _iterator6 = _createForOfIteratorHelper(this.uniforms[shaderStage]),
                _step6;

            try {
              for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
                var uniform = _step6.value;
                this.shader.uniforms[uniform.name] = uniform;
              }
            } catch (err) {
              _iterator6.e(err);
            } finally {
              _iterator6.f();
            }
          }
        } catch (err) {
          _iterator5.e(err);
        } finally {
          _iterator5.f();
        }
      }
    }]);

    return WebGLNodeBuilder;
  }(_NodeBuilder2.default);

  _exports.WebGLNodeBuilder = WebGLNodeBuilder;
});