text: /**
   * Loads a Wavefront .mtl file specifying materials
   *
   * @author angelxuanchang
   */
  
  THREE.MTLLoader = function (manager) {
  
      this.manager = (manager !== undefined) ? manager : THREE.DefaultLoadingManager;
  
  };
  
  Object.assign(THREE.MTLLoader.prototype, THREE.EventDispatcher.prototype, {
  
      /**
  	 * Loads and parses a MTL asset from a URL.
  	 *
  	 * @param {String} url - URL to the MTL file.
  	 * @param {Function} [onLoad] - Callback invoked with the loaded object.
  	 * @param {Function} [onProgress] - Callback for download progress.
  	 * @param {Function} [onError] - Callback for download errors.
  	 *
  	 * @see setPath setTexturePath
  	 *
  	 * @note In order for relative textures references to resolve correctly
  	 * you must call setPath and/or setTexturePath explicitly prior to load.
  	 */
      load: function (url, onLoad, onProgress, onError) {
  
          var scope = this;
  
          var loader = new THREE.XHRLoader(this.manager);
          loader.setPath(this.path);
          loader.load(url, function (text) {
  
              onLoad(scope.parse(text));
  
          }, onProgress, onError);
  
      },
  
      /**
  	 * Set base path for resolving references.
  	 * If set this path will be prepended to each loaded and found reference.
  	 *
  	 * @see setTexturePath
  	 * @param {String} path
  	 *
  	 * @example
  	 *     mtlLoader.setPath( 'assets/obj/' );
  	 *     mtlLoader.load( 'my.mtl', ... );
  	 */
      setPath: function (path) {
  
          this.path = path;
  
      },
  
      /**
  	 * Set base path for resolving textures references.
  	 * If set this path will be prepended found textures reference.
  	 * If not set and setPath is, it will be used as textures base path.
  	 *
  	 * @see setPath
  	 * @param {String} path
  	 *
  	 * @example
  	 *     mtlLoader.setPath( 'assets/obj/' );
  	 *     mtlLoader.setTexturePath( 'assets/textures/' );
  	 *     mtlLoader.load( 'my.mtl', ... );
  	 */
      setTexturePath: function (path) {
  
          this.texturePath = path;
  
      },
  
      setBaseUrl: function (path) {
  
          console.warn('THREE.MTLLoader: .setBaseUrl() is deprecated. Use .setTexturePath( path ) for textures path or .setPath( path ) for general base path instead.');
  
          this.setTexturePath(path);
  
      },
  
      setCrossOrigin: function (value) {
  
          this.crossOrigin = value;
  
      },
  
      setMaterialOptions: function (value) {
  
          this.materialOptions = value;
  
      },
  
      /**
  	 * Parses a MTL file.
  	 *
  	 * @param {String} text - Content of MTL file
  	 * @return {THREE.MTLLoader.MaterialCreator}
  	 *
  	 * @see setPath setTexturePath
  	 *
  	 * @note In order for relative textures references to resolve correctly
  	 * you must call setPath and/or setTexturePath explicitly prior to parse.
  	 */
      parse: function (text) {
  
          var lines = text.split('\n');
          var info = {};
          var delimiter_pattern = /\s+/;
          var materialsInfo = {};
  
          for (var i = 0; i < lines.length; i++) {
  
              var line = lines[i];
              line = line.trim();
  
              if (line.length === 0 || line.charAt(0) === '#') {
  
                  // Blank line or comment ignore
                  continue;
  
              }
  
              var pos = line.indexOf(' ');
  
              var key = (pos >= 0) ? line.substring(0, pos) : line;
              key = key.toLowerCase();
  
              var value = (pos >= 0) ? line.substring(pos + 1) : '';
              value = value.trim();
  
              if (key === 'newmtl') {
  
                  // New material
  
                  info = { name: value };
                  materialsInfo[value] = info;
  
              } else if (info) {
  
                  if (key === 'ka' || key === 'kd' || key === 'ks') {
  
                      var ss = value.split(delimiter_pattern, 3);
                      info[key] = [parseFloat(ss[0]), parseFloat(ss[1]), parseFloat(ss[2])];
  
                  } else {
  
                      info[key] = value;
  
                  }
  
              }
  
          }
  
          var materialCreator = new THREE.MTLLoader.MaterialCreator(this.texturePath || this.path, this.materialOptions);
          materialCreator.setCrossOrigin(this.crossOrigin);
          materialCreator.setManager(this.manager);
          materialCreator.setMaterials(materialsInfo);
          return materialCreator;
  
      }
  
  });
  
  /**
   * Create a new THREE-MTLLoader.MaterialCreator
   * @param baseUrl - Url relative to which textures are loaded
   * @param options - Set of options on how to construct the materials
   *                  side: Which side to apply the material
   *                        THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
   *                  wrap: What type of wrapping to apply for textures
   *                        THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
   *                  normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
   *                                Default: false, assumed to be already normalized
   *                  ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
   *                                  Default: false
   * @constructor
   */
  
  THREE.MTLLoader.MaterialCreator = function (baseUrl, options) {
  
      this.baseUrl = baseUrl || '';
      this.options = options;
      this.materialsInfo = {};
      this.materials = {};
      this.materialsArray = [];
      this.nameLookup = {};
  
      this.side = (this.options && this.options.side) ? this.options.side : THREE.FrontSide;
      this.wrap = (this.options && this.options.wrap) ? this.options.wrap : THREE.RepeatWrapping;
  
  };
  
  THREE.MTLLoader.MaterialCreator.prototype = {
  
      constructor: THREE.MTLLoader.MaterialCreator,
  
      setCrossOrigin: function (value) {
  
          this.crossOrigin = value;
  
      },
  
      setManager: function (value) {
  
          this.manager = value;
  
      },
  
      setMaterials: function (materialsInfo) {
  
          this.materialsInfo = this.convert(materialsInfo);
          this.materials = {};
          this.materialsArray = [];
          this.nameLookup = {};
  
      },
  
      convert: function (materialsInfo) {
  
          if (!this.options) return materialsInfo;
  
          var converted = {};
  
          for (var mn in materialsInfo) {
  
              // Convert materials info into normalized form based on options
  
              var mat = materialsInfo[mn];
  
              var covmat = {};
  
              converted[mn] = covmat;
  
              for (var prop in mat) {
  
                  var save = true;
                  var value = mat[prop];
                  var lprop = prop.toLowerCase();
  
                  switch (lprop) {
  
                      case 'kd':
                      case 'ka':
                      case 'ks':
  
                          // Diffuse color (color under white light) using RGB values
  
                          if (this.options && this.options.normalizeRGB) {
  
                              value = [value[0] / 255, value[1] / 255, value[2] / 255];
  
                          }
  
                          if (this.options && this.options.ignoreZeroRGBs) {
  
                              if (value[0] === 0 && value[1] === 0 && value[2] === 0) {
  
                                  // ignore
  
                                  save = false;
  
                              }
  
                          }
  
                          break;
  
                      default:
  
                          break;
                  }
  
                  if (save) {
  
                      covmat[lprop] = value;
  
                  }
  
              }
  
          }
  
          return converted;
  
      },
  
      preload: function () {
  
          for (var mn in this.materialsInfo) {
  
              this.create(mn);
  
          }
  
      },
  
      getIndex: function (materialName) {
  
          return this.nameLookup[materialName];
  
      },
  
      getAsArray: function () {
  
          var index = 0;
  
          for (var mn in this.materialsInfo) {
  
              this.materialsArray[index] = this.create(mn);
              this.nameLookup[mn] = index;
              index++;
  
          }
  
          return this.materialsArray;
  
      },
  
      create: function (materialName) {
  
          if (this.materials[materialName] === undefined) {
  
              this.createMaterial_(materialName);
  
          }
  
          return this.materials[materialName];
  
      },
  
      createMaterial_: function (materialName) {
  
          // Create material
  
          var scope = this;
          var mat = this.materialsInfo[materialName];
          var params = {
  
              name: materialName,
              side: this.side
  
          };
  
          var resolveURL = function (baseUrl, url) {
  
              if (typeof url !== 'string' || url === '')
                  return '';
  
              // Absolute URL
              if (/^https?:\/\//i.test(url)) {
                  return url;
              }
  
              return baseUrl + url;
          };
  
          function setMapForType(mapType, value) {
  
              if (params[mapType]) return; // Keep the first encountered textures
  
              var texParams = scope.getTextureParams(value, params);
              var map = scope.loadTexture(resolveURL(scope.baseUrl, texParams.url));
  
              map.repeat.copy(texParams.scale);
              map.offset.copy(texParams.offset);
  
              map.wrapS = scope.wrap;
              map.wrapT = scope.wrap;
  
              params[mapType] = map;
          }
  
          for (var prop in mat) {
  
              var value = mat[prop];
  
              if (value === '') continue;
  
              switch (prop.toLowerCase()) {
  
                  // Ns is material specular exponent
  
                  case 'kd':
  
                      // Diffuse color (color under white light) using RGB values
  
                      params.color = new THREE.Color().fromArray(value);
  
                      break;
  
                  case 'ks':
  
                      // Specular color (color when light is reflected from shiny surface) using RGB values
                      params.specular = new THREE.Color().fromArray(value);
  
                      break;
  
                  case 'map_kd':
  
                      // Diffuse textures map
  
                      setMapForType("map", value);
  
                      break;
  
                  case 'map_ks':
  
                      // Specular map
  
                      setMapForType("specularMap", value);
  
                      break;
  
                  case 'map_bump':
                  case 'bump':
  
                      // Bump textures map
  
                      setMapForType("bumpMap", value);
  
                      break;
  
                  case 'ns':
  
                      // The specular exponent (defines the focus of the specular highlight)
                      // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
  
                      params.shininess = parseFloat(value);
  
                      break;
  
                  case 'd':
  
                      if (value < 1) {
  
                          params.opacity = value;
                          params.transparent = true;
  
                      }
  
                      break;
  
                  case 'Tr':
  
                      if (value > 0) {
  
                          params.opacity = 1 - value;
                          params.transparent = true;
  
                      }
  
                      break;
  
                  default:
                      break;
  
              }
  
          }
  
          this.materials[materialName] = new THREE.MeshPhongMaterial(params);
          return this.materials[materialName];
      },
  
      getTextureParams: function (value, matParams) {
  
          var texParams = {
  
              scale: new THREE.Vector2(1, 1),
              offset: new THREE.Vector2(0, 0),
  
          };
  
          var items = value.split(/\s+/);
          var pos;
  
          pos = items.indexOf('-bm');
          if (pos >= 0) {
  
              matParams.bumpScale = parseFloat(items[pos + 1]);
              items.splice(pos, 2);
  
          }
  
          pos = items.indexOf('-s');
          if (pos >= 0) {
  
              texParams.scale.set(parseFloat(items[pos + 1]), parseFloat(items[pos + 2]));
              items.splice(pos, 4); // we expect 3 parameters here!
  
          }
  
          pos = items.indexOf('-o');
          if (pos >= 0) {
  
              texParams.offset.set(parseFloat(items[pos + 1]), parseFloat(items[pos + 2]));
              items.splice(pos, 4); // we expect 3 parameters here!
  
          }
  
          texParams.url = items.join(' ').trim();
          return texParams;
  
      },
  
      loadTexture: function (url, mapping, onLoad, onProgress, onError) {
  
          var texture;
          var loader = THREE.Loader.Handlers.get(url);
          var manager = (this.manager !== undefined) ? this.manager : THREE.DefaultLoadingManager;
  
          if (loader === null) {
  
              loader = new THREE.TextureLoader(manager);
  
          }
  
          if (loader.setCrossOrigin) loader.setCrossOrigin(this.crossOrigin);
          texture = loader.load(url, onLoad, onProgress, onError);
  
          if (mapping !== undefined) texture.mapping = mapping;
  
          return texture;
  
      }
  
  };
  