Skip to content

Instantly share code, notes, and snippets.

@dkozma
Created April 3, 2017 19:05
Show Gist options
  • Save dkozma/4527ca9ad86f2650547d50ed068d63e1 to your computer and use it in GitHub Desktop.
Save dkozma/4527ca9ad86f2650547d50ed068d63e1 to your computer and use it in GitHub Desktop.
This file has been truncated, but you can view the full file.
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 141);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/* unknown exports provided */
/* all exports used */
/*!********************************!*\
!*** ./~/three/build/three.js ***!
\********************************/
/***/ (function(module, exports, __webpack_require__) {
eval("(function (global, factory) {\n\t true ? factory(exports) :\n\ttypeof define === 'function' && define.amd ? define(['exports'], factory) :\n\t(factory((global.THREE = global.THREE || {})));\n}(this, (function (exports) { 'use strict';\n\n\t// Polyfills\n\n\tif ( Number.EPSILON === undefined ) {\n\n\t\tNumber.EPSILON = Math.pow( 2, - 52 );\n\n\t}\n\n\t//\n\n\tif ( Math.sign === undefined ) {\n\n\t\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign\n\n\t\tMath.sign = function ( x ) {\n\n\t\t\treturn ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;\n\n\t\t};\n\n\t}\n\n\tif ( Function.prototype.name === undefined ) {\n\n\t\t// Missing in IE9-11.\n\t\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name\n\n\t\tObject.defineProperty( Function.prototype, 'name', {\n\n\t\t\tget: function () {\n\n\t\t\t\treturn this.toString().match( /^\\s*function\\s*(\\S*)\\s*\\(/ )[ 1 ];\n\n\t\t\t}\n\n\t\t} );\n\n\t}\n\n\tif ( Object.assign === undefined ) {\n\n\t\t// Missing in IE.\n\t\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\n\n\t\t( function () {\n\n\t\t\tObject.assign = function ( target ) {\n\n\t\t\t\t'use strict';\n\n\t\t\t\tif ( target === undefined || target === null ) {\n\n\t\t\t\t\tthrow new TypeError( 'Cannot convert undefined or null to object' );\n\n\t\t\t\t}\n\n\t\t\t\tvar output = Object( target );\n\n\t\t\t\tfor ( var index = 1; index < arguments.length; index ++ ) {\n\n\t\t\t\t\tvar source = arguments[ index ];\n\n\t\t\t\t\tif ( source !== undefined && source !== null ) {\n\n\t\t\t\t\t\tfor ( var nextKey in source ) {\n\n\t\t\t\t\t\t\tif ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) {\n\n\t\t\t\t\t\t\t\toutput[ nextKey ] = source[ nextKey ];\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn output;\n\n\t\t\t};\n\n\t\t} )();\n\n\t}\n\n\t/**\n\t * https://github.com/mrdoob/eventdispatcher.js/\n\t */\n\n\tfunction EventDispatcher() {}\n\n\tObject.assign( EventDispatcher.prototype, {\n\n\t\taddEventListener: function ( type, listener ) {\n\n\t\t\tif ( this._listeners === undefined ) this._listeners = {};\n\n\t\t\tvar listeners = this._listeners;\n\n\t\t\tif ( listeners[ type ] === undefined ) {\n\n\t\t\t\tlisteners[ type ] = [];\n\n\t\t\t}\n\n\t\t\tif ( listeners[ type ].indexOf( listener ) === - 1 ) {\n\n\t\t\t\tlisteners[ type ].push( listener );\n\n\t\t\t}\n\n\t\t},\n\n\t\thasEventListener: function ( type, listener ) {\n\n\t\t\tif ( this._listeners === undefined ) return false;\n\n\t\t\tvar listeners = this._listeners;\n\n\t\t\tif ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {\n\n\t\t\t\treturn true;\n\n\t\t\t}\n\n\t\t\treturn false;\n\n\t\t},\n\n\t\tremoveEventListener: function ( type, listener ) {\n\n\t\t\tif ( this._listeners === undefined ) return;\n\n\t\t\tvar listeners = this._listeners;\n\t\t\tvar listenerArray = listeners[ type ];\n\n\t\t\tif ( listenerArray !== undefined ) {\n\n\t\t\t\tvar index = listenerArray.indexOf( listener );\n\n\t\t\t\tif ( index !== - 1 ) {\n\n\t\t\t\t\tlistenerArray.splice( index, 1 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t},\n\n\t\tdispatchEvent: function ( event ) {\n\n\t\t\tif ( this._listeners === undefined ) return;\n\n\t\t\tvar listeners = this._listeners;\n\t\t\tvar listenerArray = listeners[ event.type ];\n\n\t\t\tif ( listenerArray !== undefined ) {\n\n\t\t\t\tevent.target = this;\n\n\t\t\t\tvar array = [], i = 0;\n\t\t\t\tvar length = listenerArray.length;\n\n\t\t\t\tfor ( i = 0; i < length; i ++ ) {\n\n\t\t\t\t\tarray[ i ] = listenerArray[ i ];\n\n\t\t\t\t}\n\n\t\t\t\tfor ( i = 0; i < length; i ++ ) {\n\n\t\t\t\t\tarray[ i ].call( this, event );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t} );\n\n\tvar REVISION = '82';\n\tvar MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };\n\tvar CullFaceNone = 0;\n\tvar CullFaceBack = 1;\n\tvar CullFaceFront = 2;\n\tvar CullFaceFrontBack = 3;\n\tvar FrontFaceDirectionCW = 0;\n\tvar FrontFaceDirectionCCW = 1;\n\tvar BasicShadowMap = 0;\n\tvar PCFShadowMap = 1;\n\tvar PCFSoftShadowMap = 2;\n\tvar FrontSide = 0;\n\tvar BackSide = 1;\n\tvar DoubleSide = 2;\n\tvar FlatShading = 1;\n\tvar SmoothShading = 2;\n\tvar NoColors = 0;\n\tvar FaceColors = 1;\n\tvar VertexColors = 2;\n\tvar NoBlending = 0;\n\tvar NormalBlending = 1;\n\tvar AdditiveBlending = 2;\n\tvar SubtractiveBlending = 3;\n\tvar MultiplyBlending = 4;\n\tvar CustomBlending = 5;\n\tvar BlendingMode = {\n\t\tNoBlending: NoBlending,\n\t\tNormalBlending: NormalBlending,\n\t\tAdditiveBlending: AdditiveBlending,\n\t\tSubtractiveBlending: SubtractiveBlending,\n\t\tMultiplyBlending: MultiplyBlending,\n\t\tCustomBlending: CustomBlending\n\t};\n\tvar AddEquation = 100;\n\tvar SubtractEquation = 101;\n\tvar ReverseSubtractEquation = 102;\n\tvar MinEquation = 103;\n\tvar MaxEquation = 104;\n\tvar ZeroFactor = 200;\n\tvar OneFactor = 201;\n\tvar SrcColorFactor = 202;\n\tvar OneMinusSrcColorFactor = 203;\n\tvar SrcAlphaFactor = 204;\n\tvar OneMinusSrcAlphaFactor = 205;\n\tvar DstAlphaFactor = 206;\n\tvar OneMinusDstAlphaFactor = 207;\n\tvar DstColorFactor = 208;\n\tvar OneMinusDstColorFactor = 209;\n\tvar SrcAlphaSaturateFactor = 210;\n\tvar NeverDepth = 0;\n\tvar AlwaysDepth = 1;\n\tvar LessDepth = 2;\n\tvar LessEqualDepth = 3;\n\tvar EqualDepth = 4;\n\tvar GreaterEqualDepth = 5;\n\tvar GreaterDepth = 6;\n\tvar NotEqualDepth = 7;\n\tvar MultiplyOperation = 0;\n\tvar MixOperation = 1;\n\tvar AddOperation = 2;\n\tvar NoToneMapping = 0;\n\tvar LinearToneMapping = 1;\n\tvar ReinhardToneMapping = 2;\n\tvar Uncharted2ToneMapping = 3;\n\tvar CineonToneMapping = 4;\n\tvar UVMapping = 300;\n\tvar CubeReflectionMapping = 301;\n\tvar CubeRefractionMapping = 302;\n\tvar EquirectangularReflectionMapping = 303;\n\tvar EquirectangularRefractionMapping = 304;\n\tvar SphericalReflectionMapping = 305;\n\tvar CubeUVReflectionMapping = 306;\n\tvar CubeUVRefractionMapping = 307;\n\tvar TextureMapping = {\n\t\tUVMapping: UVMapping,\n\t\tCubeReflectionMapping: CubeReflectionMapping,\n\t\tCubeRefractionMapping: CubeRefractionMapping,\n\t\tEquirectangularReflectionMapping: EquirectangularReflectionMapping,\n\t\tEquirectangularRefractionMapping: EquirectangularRefractionMapping,\n\t\tSphericalReflectionMapping: SphericalReflectionMapping,\n\t\tCubeUVReflectionMapping: CubeUVReflectionMapping,\n\t\tCubeUVRefractionMapping: CubeUVRefractionMapping\n\t};\n\tvar RepeatWrapping = 1000;\n\tvar ClampToEdgeWrapping = 1001;\n\tvar MirroredRepeatWrapping = 1002;\n\tvar TextureWrapping = {\n\t\tRepeatWrapping: RepeatWrapping,\n\t\tClampToEdgeWrapping: ClampToEdgeWrapping,\n\t\tMirroredRepeatWrapping: MirroredRepeatWrapping\n\t};\n\tvar NearestFilter = 1003;\n\tvar NearestMipMapNearestFilter = 1004;\n\tvar NearestMipMapLinearFilter = 1005;\n\tvar LinearFilter = 1006;\n\tvar LinearMipMapNearestFilter = 1007;\n\tvar LinearMipMapLinearFilter = 1008;\n\tvar TextureFilter = {\n\t\tNearestFilter: NearestFilter,\n\t\tNearestMipMapNearestFilter: NearestMipMapNearestFilter,\n\t\tNearestMipMapLinearFilter: NearestMipMapLinearFilter,\n\t\tLinearFilter: LinearFilter,\n\t\tLinearMipMapNearestFilter: LinearMipMapNearestFilter,\n\t\tLinearMipMapLinearFilter: LinearMipMapLinearFilter\n\t};\n\tvar UnsignedByteType = 1009;\n\tvar ByteType = 1010;\n\tvar ShortType = 1011;\n\tvar UnsignedShortType = 1012;\n\tvar IntType = 1013;\n\tvar UnsignedIntType = 1014;\n\tvar FloatType = 1015;\n\tvar HalfFloatType = 1016;\n\tvar UnsignedShort4444Type = 1017;\n\tvar UnsignedShort5551Type = 1018;\n\tvar UnsignedShort565Type = 1019;\n\tvar UnsignedInt248Type = 1020;\n\tvar AlphaFormat = 1021;\n\tvar RGBFormat = 1022;\n\tvar RGBAFormat = 1023;\n\tvar LuminanceFormat = 1024;\n\tvar LuminanceAlphaFormat = 1025;\n\tvar RGBEFormat = RGBAFormat;\n\tvar DepthFormat = 1026;\n\tvar DepthStencilFormat = 1027;\n\tvar RGB_S3TC_DXT1_Format = 2001;\n\tvar RGBA_S3TC_DXT1_Format = 2002;\n\tvar RGBA_S3TC_DXT3_Format = 2003;\n\tvar RGBA_S3TC_DXT5_Format = 2004;\n\tvar RGB_PVRTC_4BPPV1_Format = 2100;\n\tvar RGB_PVRTC_2BPPV1_Format = 2101;\n\tvar RGBA_PVRTC_4BPPV1_Format = 2102;\n\tvar RGBA_PVRTC_2BPPV1_Format = 2103;\n\tvar RGB_ETC1_Format = 2151;\n\tvar LoopOnce = 2200;\n\tvar LoopRepeat = 2201;\n\tvar LoopPingPong = 2202;\n\tvar InterpolateDiscrete = 2300;\n\tvar InterpolateLinear = 2301;\n\tvar InterpolateSmooth = 2302;\n\tvar ZeroCurvatureEnding = 2400;\n\tvar ZeroSlopeEnding = 2401;\n\tvar WrapAroundEnding = 2402;\n\tvar TrianglesDrawMode = 0;\n\tvar TriangleStripDrawMode = 1;\n\tvar TriangleFanDrawMode = 2;\n\tvar LinearEncoding = 3000;\n\tvar sRGBEncoding = 3001;\n\tvar GammaEncoding = 3007;\n\tvar RGBEEncoding = 3002;\n\tvar LogLuvEncoding = 3003;\n\tvar RGBM7Encoding = 3004;\n\tvar RGBM16Encoding = 3005;\n\tvar RGBDEncoding = 3006;\n\tvar BasicDepthPacking = 3200;\n\tvar RGBADepthPacking = 3201;\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tvar _Math = {\n\n\t\tDEG2RAD: Math.PI / 180,\n\t\tRAD2DEG: 180 / Math.PI,\n\n\t\tgenerateUUID: function () {\n\n\t\t\t// http://www.broofa.com/Tools/Math.uuid.htm\n\n\t\t\tvar chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );\n\t\t\tvar uuid = new Array( 36 );\n\t\t\tvar rnd = 0, r;\n\n\t\t\treturn function generateUUID() {\n\n\t\t\t\tfor ( var i = 0; i < 36; i ++ ) {\n\n\t\t\t\t\tif ( i === 8 || i === 13 || i === 18 || i === 23 ) {\n\n\t\t\t\t\t\tuuid[ i ] = '-';\n\n\t\t\t\t\t} else if ( i === 14 ) {\n\n\t\t\t\t\t\tuuid[ i ] = '4';\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;\n\t\t\t\t\t\tr = rnd & 0xf;\n\t\t\t\t\t\trnd = rnd >> 4;\n\t\t\t\t\t\tuuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn uuid.join( '' );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclamp: function ( value, min, max ) {\n\n\t\t\treturn Math.max( min, Math.min( max, value ) );\n\n\t\t},\n\n\t\t// compute euclidian modulo of m % n\n\t\t// https://en.wikipedia.org/wiki/Modulo_operation\n\n\t\teuclideanModulo: function ( n, m ) {\n\n\t\t\treturn ( ( n % m ) + m ) % m;\n\n\t\t},\n\n\t\t// Linear mapping from range <a1, a2> to range <b1, b2>\n\n\t\tmapLinear: function ( x, a1, a2, b1, b2 ) {\n\n\t\t\treturn b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );\n\n\t\t},\n\n\t\t// https://en.wikipedia.org/wiki/Linear_interpolation\n\n\t\tlerp: function ( x, y, t ) {\n\n\t\t\treturn ( 1 - t ) * x + t * y;\n\n\t\t},\n\n\t\t// http://en.wikipedia.org/wiki/Smoothstep\n\n\t\tsmoothstep: function ( x, min, max ) {\n\n\t\t\tif ( x <= min ) return 0;\n\t\t\tif ( x >= max ) return 1;\n\n\t\t\tx = ( x - min ) / ( max - min );\n\n\t\t\treturn x * x * ( 3 - 2 * x );\n\n\t\t},\n\n\t\tsmootherstep: function ( x, min, max ) {\n\n\t\t\tif ( x <= min ) return 0;\n\t\t\tif ( x >= max ) return 1;\n\n\t\t\tx = ( x - min ) / ( max - min );\n\n\t\t\treturn x * x * x * ( x * ( x * 6 - 15 ) + 10 );\n\n\t\t},\n\n\t\trandom16: function () {\n\n\t\t\tconsole.warn( 'THREE.Math.random16() has been deprecated. Use Math.random() instead.' );\n\t\t\treturn Math.random();\n\n\t\t},\n\n\t\t// Random integer from <low, high> interval\n\n\t\trandInt: function ( low, high ) {\n\n\t\t\treturn low + Math.floor( Math.random() * ( high - low + 1 ) );\n\n\t\t},\n\n\t\t// Random float from <low, high> interval\n\n\t\trandFloat: function ( low, high ) {\n\n\t\t\treturn low + Math.random() * ( high - low );\n\n\t\t},\n\n\t\t// Random float from <-range/2, range/2> interval\n\n\t\trandFloatSpread: function ( range ) {\n\n\t\t\treturn range * ( 0.5 - Math.random() );\n\n\t\t},\n\n\t\tdegToRad: function ( degrees ) {\n\n\t\t\treturn degrees * _Math.DEG2RAD;\n\n\t\t},\n\n\t\tradToDeg: function ( radians ) {\n\n\t\t\treturn radians * _Math.RAD2DEG;\n\n\t\t},\n\n\t\tisPowerOfTwo: function ( value ) {\n\n\t\t\treturn ( value & ( value - 1 ) ) === 0 && value !== 0;\n\n\t\t},\n\n\t\tnearestPowerOfTwo: function ( value ) {\n\n\t\t\treturn Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) );\n\n\t\t},\n\n\t\tnextPowerOfTwo: function ( value ) {\n\n\t\t\tvalue --;\n\t\t\tvalue |= value >> 1;\n\t\t\tvalue |= value >> 2;\n\t\t\tvalue |= value >> 4;\n\t\t\tvalue |= value >> 8;\n\t\t\tvalue |= value >> 16;\n\t\t\tvalue ++;\n\n\t\t\treturn value;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author philogb / http://blog.thejit.org/\n\t * @author egraether / http://egraether.com/\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t */\n\n\tfunction Vector2( x, y ) {\n\n\t\tthis.x = x || 0;\n\t\tthis.y = y || 0;\n\n\t}\n\n\tVector2.prototype = {\n\n\t\tconstructor: Vector2,\n\n\t\tisVector2: true,\n\n\t\tget width() {\n\n\t\t\treturn this.x;\n\n\t\t},\n\n\t\tset width( value ) {\n\n\t\t\tthis.x = value;\n\n\t\t},\n\n\t\tget height() {\n\n\t\t\treturn this.y;\n\n\t\t},\n\n\t\tset height( value ) {\n\n\t\t\tthis.y = value;\n\n\t\t},\n\n\t\t//\n\n\t\tset: function ( x, y ) {\n\n\t\t\tthis.x = x;\n\t\t\tthis.y = y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetScalar: function ( scalar ) {\n\n\t\t\tthis.x = scalar;\n\t\t\tthis.y = scalar;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetX: function ( x ) {\n\n\t\t\tthis.x = x;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetY: function ( y ) {\n\n\t\t\tthis.y = y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetComponent: function ( index, value ) {\n\n\t\t\tswitch ( index ) {\n\n\t\t\t\tcase 0: this.x = value; break;\n\t\t\t\tcase 1: this.y = value; break;\n\t\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t\t}\n\t\t\t\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetComponent: function ( index ) {\n\n\t\t\tswitch ( index ) {\n\n\t\t\t\tcase 0: return this.x;\n\t\t\t\tcase 1: return this.y;\n\t\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t\t}\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.x, this.y );\n\n\t\t},\n\n\t\tcopy: function ( v ) {\n\n\t\t\tthis.x = v.x;\n\t\t\tthis.y = v.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tadd: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n\t\t\t\treturn this.addVectors( v, w );\n\n\t\t\t}\n\n\t\t\tthis.x += v.x;\n\t\t\tthis.y += v.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddScalar: function ( s ) {\n\n\t\t\tthis.x += s;\n\t\t\tthis.y += s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddVectors: function ( a, b ) {\n\n\t\t\tthis.x = a.x + b.x;\n\t\t\tthis.y = a.y + b.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddScaledVector: function ( v, s ) {\n\n\t\t\tthis.x += v.x * s;\n\t\t\tthis.y += v.y * s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsub: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n\t\t\t\treturn this.subVectors( v, w );\n\n\t\t\t}\n\n\t\t\tthis.x -= v.x;\n\t\t\tthis.y -= v.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsubScalar: function ( s ) {\n\n\t\t\tthis.x -= s;\n\t\t\tthis.y -= s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsubVectors: function ( a, b ) {\n\n\t\t\tthis.x = a.x - b.x;\n\t\t\tthis.y = a.y - b.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiply: function ( v ) {\n\n\t\t\tthis.x *= v.x;\n\t\t\tthis.y *= v.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiplyScalar: function ( scalar ) {\n\n\t\t\tif ( isFinite( scalar ) ) {\n\n\t\t\t\tthis.x *= scalar;\n\t\t\t\tthis.y *= scalar;\n\n\t\t\t} else {\n\n\t\t\t\tthis.x = 0;\n\t\t\t\tthis.y = 0;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdivide: function ( v ) {\n\n\t\t\tthis.x /= v.x;\n\t\t\tthis.y /= v.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdivideScalar: function ( scalar ) {\n\n\t\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t\t},\n\n\t\tmin: function ( v ) {\n\n\t\t\tthis.x = Math.min( this.x, v.x );\n\t\t\tthis.y = Math.min( this.y, v.y );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmax: function ( v ) {\n\n\t\t\tthis.x = Math.max( this.x, v.x );\n\t\t\tthis.y = Math.max( this.y, v.y );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclamp: function ( min, max ) {\n\n\t\t\t// This function assumes min < max, if this assumption isn't true it will not operate correctly\n\n\t\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclampScalar: function () {\n\n\t\t\tvar min, max;\n\n\t\t\treturn function clampScalar( minVal, maxVal ) {\n\n\t\t\t\tif ( min === undefined ) {\n\n\t\t\t\t\tmin = new Vector2();\n\t\t\t\t\tmax = new Vector2();\n\n\t\t\t\t}\n\n\t\t\t\tmin.set( minVal, minVal );\n\t\t\t\tmax.set( maxVal, maxVal );\n\n\t\t\t\treturn this.clamp( min, max );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclampLength: function ( min, max ) {\n\n\t\t\tvar length = this.length();\n\n\t\t\treturn this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );\n\n\t\t},\n\n\t\tfloor: function () {\n\n\t\t\tthis.x = Math.floor( this.x );\n\t\t\tthis.y = Math.floor( this.y );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tceil: function () {\n\n\t\t\tthis.x = Math.ceil( this.x );\n\t\t\tthis.y = Math.ceil( this.y );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tround: function () {\n\n\t\t\tthis.x = Math.round( this.x );\n\t\t\tthis.y = Math.round( this.y );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\troundToZero: function () {\n\n\t\t\tthis.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n\t\t\tthis.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tnegate: function () {\n\n\t\t\tthis.x = - this.x;\n\t\t\tthis.y = - this.y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdot: function ( v ) {\n\n\t\t\treturn this.x * v.x + this.y * v.y;\n\n\t\t},\n\n\t\tlengthSq: function () {\n\n\t\t\treturn this.x * this.x + this.y * this.y;\n\n\t\t},\n\n\t\tlength: function () {\n\n\t\t\treturn Math.sqrt( this.x * this.x + this.y * this.y );\n\n\t\t},\n\n\t\tlengthManhattan: function() {\n\n\t\t\treturn Math.abs( this.x ) + Math.abs( this.y );\n\n\t\t},\n\n\t\tnormalize: function () {\n\n\t\t\treturn this.divideScalar( this.length() );\n\n\t\t},\n\n\t\tangle: function () {\n\n\t\t\t// computes the angle in radians with respect to the positive x-axis\n\n\t\t\tvar angle = Math.atan2( this.y, this.x );\n\n\t\t\tif ( angle < 0 ) angle += 2 * Math.PI;\n\n\t\t\treturn angle;\n\n\t\t},\n\n\t\tdistanceTo: function ( v ) {\n\n\t\t\treturn Math.sqrt( this.distanceToSquared( v ) );\n\n\t\t},\n\n\t\tdistanceToSquared: function ( v ) {\n\n\t\t\tvar dx = this.x - v.x, dy = this.y - v.y;\n\t\t\treturn dx * dx + dy * dy;\n\n\t\t},\n\n\t\tdistanceToManhattan: function ( v ) {\n\n\t\t\treturn Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );\n\n\t\t},\n\n\t\tsetLength: function ( length ) {\n\n\t\t\treturn this.multiplyScalar( length / this.length() );\n\n\t\t},\n\n\t\tlerp: function ( v, alpha ) {\n\n\t\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\t\tthis.y += ( v.y - this.y ) * alpha;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tlerpVectors: function ( v1, v2, alpha ) {\n\n\t\t\treturn this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n\t\t},\n\n\t\tequals: function ( v ) {\n\n\t\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) );\n\n\t\t},\n\n\t\tfromArray: function ( array, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tthis.x = array[ offset ];\n\t\t\tthis.y = array[ offset + 1 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tarray[ offset ] = this.x;\n\t\t\tarray[ offset + 1 ] = this.y;\n\n\t\t\treturn array;\n\n\t\t},\n\n\t\tfromAttribute: function ( attribute, index, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tindex = index * attribute.itemSize + offset;\n\n\t\t\tthis.x = attribute.array[ index ];\n\t\t\tthis.y = attribute.array[ index + 1 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\trotateAround: function ( center, angle ) {\n\n\t\t\tvar c = Math.cos( angle ), s = Math.sin( angle );\n\n\t\t\tvar x = this.x - center.x;\n\t\t\tvar y = this.y - center.y;\n\n\t\t\tthis.x = x * c - y * s + center.x;\n\t\t\tthis.y = x * s + y * c + center.y;\n\n\t\t\treturn this;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author szimek / https://github.com/szimek/\n\t */\n\n\tfunction Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {\n\n\t\tObject.defineProperty( this, 'id', { value: TextureIdCount() } );\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.sourceFile = '';\n\n\t\tthis.image = image !== undefined ? image : Texture.DEFAULT_IMAGE;\n\t\tthis.mipmaps = [];\n\n\t\tthis.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING;\n\n\t\tthis.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping;\n\t\tthis.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping;\n\n\t\tthis.magFilter = magFilter !== undefined ? magFilter : LinearFilter;\n\t\tthis.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter;\n\n\t\tthis.anisotropy = anisotropy !== undefined ? anisotropy : 1;\n\n\t\tthis.format = format !== undefined ? format : RGBAFormat;\n\t\tthis.type = type !== undefined ? type : UnsignedByteType;\n\n\t\tthis.offset = new Vector2( 0, 0 );\n\t\tthis.repeat = new Vector2( 1, 1 );\n\n\t\tthis.generateMipmaps = true;\n\t\tthis.premultiplyAlpha = false;\n\t\tthis.flipY = true;\n\t\tthis.unpackAlignment = 4;\t// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)\n\n\n\t\t// Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.\n\t\t//\n\t\t// Also changing the encoding after already used by a Material will not automatically make the Material\n\t\t// update. You need to explicitly call Material.needsUpdate to trigger it to recompile.\n\t\tthis.encoding = encoding !== undefined ? encoding : LinearEncoding;\n\n\t\tthis.version = 0;\n\t\tthis.onUpdate = null;\n\n\t}\n\n\tTexture.DEFAULT_IMAGE = undefined;\n\tTexture.DEFAULT_MAPPING = UVMapping;\n\n\tTexture.prototype = {\n\n\t\tconstructor: Texture,\n\n\t\tisTexture: true,\n\n\t\tset needsUpdate( value ) {\n\n\t\t\tif ( value === true ) this.version ++;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tthis.image = source.image;\n\t\t\tthis.mipmaps = source.mipmaps.slice( 0 );\n\n\t\t\tthis.mapping = source.mapping;\n\n\t\t\tthis.wrapS = source.wrapS;\n\t\t\tthis.wrapT = source.wrapT;\n\n\t\t\tthis.magFilter = source.magFilter;\n\t\t\tthis.minFilter = source.minFilter;\n\n\t\t\tthis.anisotropy = source.anisotropy;\n\n\t\t\tthis.format = source.format;\n\t\t\tthis.type = source.type;\n\n\t\t\tthis.offset.copy( source.offset );\n\t\t\tthis.repeat.copy( source.repeat );\n\n\t\t\tthis.generateMipmaps = source.generateMipmaps;\n\t\t\tthis.premultiplyAlpha = source.premultiplyAlpha;\n\t\t\tthis.flipY = source.flipY;\n\t\t\tthis.unpackAlignment = source.unpackAlignment;\n\t\t\tthis.encoding = source.encoding;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\tif ( meta.textures[ this.uuid ] !== undefined ) {\n\n\t\t\t\treturn meta.textures[ this.uuid ];\n\n\t\t\t}\n\n\t\t\tfunction getDataURL( image ) {\n\n\t\t\t\tvar canvas;\n\n\t\t\t\tif ( image.toDataURL !== undefined ) {\n\n\t\t\t\t\tcanvas = image;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tcanvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n\t\t\t\t\tcanvas.width = image.width;\n\t\t\t\t\tcanvas.height = image.height;\n\n\t\t\t\t\tcanvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height );\n\n\t\t\t\t}\n\n\t\t\t\tif ( canvas.width > 2048 || canvas.height > 2048 ) {\n\n\t\t\t\t\treturn canvas.toDataURL( 'image/jpeg', 0.6 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn canvas.toDataURL( 'image/png' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar output = {\n\t\t\t\tmetadata: {\n\t\t\t\t\tversion: 4.4,\n\t\t\t\t\ttype: 'Texture',\n\t\t\t\t\tgenerator: 'Texture.toJSON'\n\t\t\t\t},\n\n\t\t\t\tuuid: this.uuid,\n\t\t\t\tname: this.name,\n\n\t\t\t\tmapping: this.mapping,\n\n\t\t\t\trepeat: [ this.repeat.x, this.repeat.y ],\n\t\t\t\toffset: [ this.offset.x, this.offset.y ],\n\t\t\t\twrap: [ this.wrapS, this.wrapT ],\n\n\t\t\t\tminFilter: this.minFilter,\n\t\t\t\tmagFilter: this.magFilter,\n\t\t\t\tanisotropy: this.anisotropy,\n\n\t\t\t\tflipY: this.flipY\n\t\t\t};\n\n\t\t\tif ( this.image !== undefined ) {\n\n\t\t\t\t// TODO: Move to THREE.Image\n\n\t\t\t\tvar image = this.image;\n\n\t\t\t\tif ( image.uuid === undefined ) {\n\n\t\t\t\t\timage.uuid = _Math.generateUUID(); // UGH\n\n\t\t\t\t}\n\n\t\t\t\tif ( meta.images[ image.uuid ] === undefined ) {\n\n\t\t\t\t\tmeta.images[ image.uuid ] = {\n\t\t\t\t\t\tuuid: image.uuid,\n\t\t\t\t\t\turl: getDataURL( image )\n\t\t\t\t\t};\n\n\t\t\t\t}\n\n\t\t\t\toutput.image = image.uuid;\n\n\t\t\t}\n\n\t\t\tmeta.textures[ this.uuid ] = output;\n\n\t\t\treturn output;\n\n\t\t},\n\n\t\tdispose: function () {\n\n\t\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t\t},\n\n\t\ttransformUv: function ( uv ) {\n\n\t\t\tif ( this.mapping !== UVMapping ) return;\n\n\t\t\tuv.multiply( this.repeat );\n\t\t\tuv.add( this.offset );\n\n\t\t\tif ( uv.x < 0 || uv.x > 1 ) {\n\n\t\t\t\tswitch ( this.wrapS ) {\n\n\t\t\t\t\tcase RepeatWrapping:\n\n\t\t\t\t\t\tuv.x = uv.x - Math.floor( uv.x );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ClampToEdgeWrapping:\n\n\t\t\t\t\t\tuv.x = uv.x < 0 ? 0 : 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase MirroredRepeatWrapping:\n\n\t\t\t\t\t\tif ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {\n\n\t\t\t\t\t\t\tuv.x = Math.ceil( uv.x ) - uv.x;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tuv.x = uv.x - Math.floor( uv.x );\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( uv.y < 0 || uv.y > 1 ) {\n\n\t\t\t\tswitch ( this.wrapT ) {\n\n\t\t\t\t\tcase RepeatWrapping:\n\n\t\t\t\t\t\tuv.y = uv.y - Math.floor( uv.y );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ClampToEdgeWrapping:\n\n\t\t\t\t\t\tuv.y = uv.y < 0 ? 0 : 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase MirroredRepeatWrapping:\n\n\t\t\t\t\t\tif ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {\n\n\t\t\t\t\t\t\tuv.y = Math.ceil( uv.y ) - uv.y;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tuv.y = uv.y - Math.floor( uv.y );\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( this.flipY ) {\n\n\t\t\t\tuv.y = 1 - uv.y;\n\n\t\t\t}\n\n\t\t}\n\n\t};\n\n\tObject.assign( Texture.prototype, EventDispatcher.prototype );\n\n\tvar count = 0;\n\tfunction TextureIdCount() { return count++; }\n\n\t/**\n\t * @author supereggbert / http://www.paulbrunt.co.uk/\n\t * @author philogb / http://blog.thejit.org/\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author egraether / http://egraether.com/\n\t * @author WestLangley / http://github.com/WestLangley\n\t */\n\n\tfunction Vector4( x, y, z, w ) {\n\n\t\tthis.x = x || 0;\n\t\tthis.y = y || 0;\n\t\tthis.z = z || 0;\n\t\tthis.w = ( w !== undefined ) ? w : 1;\n\n\t}\n\n\tVector4.prototype = {\n\n\t\tconstructor: Vector4,\n\n\t\tisVector4: true,\n\n\t\tset: function ( x, y, z, w ) {\n\n\t\t\tthis.x = x;\n\t\t\tthis.y = y;\n\t\t\tthis.z = z;\n\t\t\tthis.w = w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetScalar: function ( scalar ) {\n\n\t\t\tthis.x = scalar;\n\t\t\tthis.y = scalar;\n\t\t\tthis.z = scalar;\n\t\t\tthis.w = scalar;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetX: function ( x ) {\n\n\t\t\tthis.x = x;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetY: function ( y ) {\n\n\t\t\tthis.y = y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetZ: function ( z ) {\n\n\t\t\tthis.z = z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetW: function ( w ) {\n\n\t\t\tthis.w = w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetComponent: function ( index, value ) {\n\n\t\t\tswitch ( index ) {\n\n\t\t\t\tcase 0: this.x = value; break;\n\t\t\t\tcase 1: this.y = value; break;\n\t\t\t\tcase 2: this.z = value; break;\n\t\t\t\tcase 3: this.w = value; break;\n\t\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t\t}\n\t\t\t\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetComponent: function ( index ) {\n\n\t\t\tswitch ( index ) {\n\n\t\t\t\tcase 0: return this.x;\n\t\t\t\tcase 1: return this.y;\n\t\t\t\tcase 2: return this.z;\n\t\t\t\tcase 3: return this.w;\n\t\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t\t}\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.x, this.y, this.z, this.w );\n\n\t\t},\n\n\t\tcopy: function ( v ) {\n\n\t\t\tthis.x = v.x;\n\t\t\tthis.y = v.y;\n\t\t\tthis.z = v.z;\n\t\t\tthis.w = ( v.w !== undefined ) ? v.w : 1;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tadd: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n\t\t\t\treturn this.addVectors( v, w );\n\n\t\t\t}\n\n\t\t\tthis.x += v.x;\n\t\t\tthis.y += v.y;\n\t\t\tthis.z += v.z;\n\t\t\tthis.w += v.w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddScalar: function ( s ) {\n\n\t\t\tthis.x += s;\n\t\t\tthis.y += s;\n\t\t\tthis.z += s;\n\t\t\tthis.w += s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddVectors: function ( a, b ) {\n\n\t\t\tthis.x = a.x + b.x;\n\t\t\tthis.y = a.y + b.y;\n\t\t\tthis.z = a.z + b.z;\n\t\t\tthis.w = a.w + b.w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddScaledVector: function ( v, s ) {\n\n\t\t\tthis.x += v.x * s;\n\t\t\tthis.y += v.y * s;\n\t\t\tthis.z += v.z * s;\n\t\t\tthis.w += v.w * s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsub: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n\t\t\t\treturn this.subVectors( v, w );\n\n\t\t\t}\n\n\t\t\tthis.x -= v.x;\n\t\t\tthis.y -= v.y;\n\t\t\tthis.z -= v.z;\n\t\t\tthis.w -= v.w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsubScalar: function ( s ) {\n\n\t\t\tthis.x -= s;\n\t\t\tthis.y -= s;\n\t\t\tthis.z -= s;\n\t\t\tthis.w -= s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsubVectors: function ( a, b ) {\n\n\t\t\tthis.x = a.x - b.x;\n\t\t\tthis.y = a.y - b.y;\n\t\t\tthis.z = a.z - b.z;\n\t\t\tthis.w = a.w - b.w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiplyScalar: function ( scalar ) {\n\n\t\t\tif ( isFinite( scalar ) ) {\n\n\t\t\t\tthis.x *= scalar;\n\t\t\t\tthis.y *= scalar;\n\t\t\t\tthis.z *= scalar;\n\t\t\t\tthis.w *= scalar;\n\n\t\t\t} else {\n\n\t\t\t\tthis.x = 0;\n\t\t\t\tthis.y = 0;\n\t\t\t\tthis.z = 0;\n\t\t\t\tthis.w = 0;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyMatrix4: function ( m ) {\n\n\t\t\tvar x = this.x, y = this.y, z = this.z, w = this.w;\n\t\t\tvar e = m.elements;\n\n\t\t\tthis.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;\n\t\t\tthis.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;\n\t\t\tthis.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;\n\t\t\tthis.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdivideScalar: function ( scalar ) {\n\n\t\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t\t},\n\n\t\tsetAxisAngleFromQuaternion: function ( q ) {\n\n\t\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm\n\n\t\t\t// q is assumed to be normalized\n\n\t\t\tthis.w = 2 * Math.acos( q.w );\n\n\t\t\tvar s = Math.sqrt( 1 - q.w * q.w );\n\n\t\t\tif ( s < 0.0001 ) {\n\n\t\t\t\t this.x = 1;\n\t\t\t\t this.y = 0;\n\t\t\t\t this.z = 0;\n\n\t\t\t} else {\n\n\t\t\t\t this.x = q.x / s;\n\t\t\t\t this.y = q.y / s;\n\t\t\t\t this.z = q.z / s;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetAxisAngleFromRotationMatrix: function ( m ) {\n\n\t\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm\n\n\t\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\t\tvar angle, x, y, z,\t\t// variables for result\n\t\t\t\tepsilon = 0.01,\t\t// margin to allow for rounding errors\n\t\t\t\tepsilon2 = 0.1,\t\t// margin to distinguish between 0 and 180 degrees\n\n\t\t\t\tte = m.elements,\n\n\t\t\t\tm11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n\t\t\t\tm21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n\t\t\t\tm31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n\t\t\tif ( ( Math.abs( m12 - m21 ) < epsilon ) &&\n\t\t\t ( Math.abs( m13 - m31 ) < epsilon ) &&\n\t\t\t ( Math.abs( m23 - m32 ) < epsilon ) ) {\n\n\t\t\t\t// singularity found\n\t\t\t\t// first check for identity matrix which must have +1 for all terms\n\t\t\t\t// in leading diagonal and zero in other terms\n\n\t\t\t\tif ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&\n\t\t\t\t ( Math.abs( m13 + m31 ) < epsilon2 ) &&\n\t\t\t\t ( Math.abs( m23 + m32 ) < epsilon2 ) &&\n\t\t\t\t ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {\n\n\t\t\t\t\t// this singularity is identity matrix so angle = 0\n\n\t\t\t\t\tthis.set( 1, 0, 0, 0 );\n\n\t\t\t\t\treturn this; // zero angle, arbitrary axis\n\n\t\t\t\t}\n\n\t\t\t\t// otherwise this singularity is angle = 180\n\n\t\t\t\tangle = Math.PI;\n\n\t\t\t\tvar xx = ( m11 + 1 ) / 2;\n\t\t\t\tvar yy = ( m22 + 1 ) / 2;\n\t\t\t\tvar zz = ( m33 + 1 ) / 2;\n\t\t\t\tvar xy = ( m12 + m21 ) / 4;\n\t\t\t\tvar xz = ( m13 + m31 ) / 4;\n\t\t\t\tvar yz = ( m23 + m32 ) / 4;\n\n\t\t\t\tif ( ( xx > yy ) && ( xx > zz ) ) {\n\n\t\t\t\t\t// m11 is the largest diagonal term\n\n\t\t\t\t\tif ( xx < epsilon ) {\n\n\t\t\t\t\t\tx = 0;\n\t\t\t\t\t\ty = 0.707106781;\n\t\t\t\t\t\tz = 0.707106781;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tx = Math.sqrt( xx );\n\t\t\t\t\t\ty = xy / x;\n\t\t\t\t\t\tz = xz / x;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( yy > zz ) {\n\n\t\t\t\t\t// m22 is the largest diagonal term\n\n\t\t\t\t\tif ( yy < epsilon ) {\n\n\t\t\t\t\t\tx = 0.707106781;\n\t\t\t\t\t\ty = 0;\n\t\t\t\t\t\tz = 0.707106781;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ty = Math.sqrt( yy );\n\t\t\t\t\t\tx = xy / y;\n\t\t\t\t\t\tz = yz / y;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// m33 is the largest diagonal term so base result on this\n\n\t\t\t\t\tif ( zz < epsilon ) {\n\n\t\t\t\t\t\tx = 0.707106781;\n\t\t\t\t\t\ty = 0.707106781;\n\t\t\t\t\t\tz = 0;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tz = Math.sqrt( zz );\n\t\t\t\t\t\tx = xz / z;\n\t\t\t\t\t\ty = yz / z;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tthis.set( x, y, z, angle );\n\n\t\t\t\treturn this; // return 180 deg rotation\n\n\t\t\t}\n\n\t\t\t// as we have reached here there are no singularities so we can handle normally\n\n\t\t\tvar s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +\n\t\t\t ( m13 - m31 ) * ( m13 - m31 ) +\n\t\t\t ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize\n\n\t\t\tif ( Math.abs( s ) < 0.001 ) s = 1;\n\n\t\t\t// prevent divide by zero, should not happen if matrix is orthogonal and should be\n\t\t\t// caught by singularity test above, but I've left it in just in case\n\n\t\t\tthis.x = ( m32 - m23 ) / s;\n\t\t\tthis.y = ( m13 - m31 ) / s;\n\t\t\tthis.z = ( m21 - m12 ) / s;\n\t\t\tthis.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmin: function ( v ) {\n\n\t\t\tthis.x = Math.min( this.x, v.x );\n\t\t\tthis.y = Math.min( this.y, v.y );\n\t\t\tthis.z = Math.min( this.z, v.z );\n\t\t\tthis.w = Math.min( this.w, v.w );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmax: function ( v ) {\n\n\t\t\tthis.x = Math.max( this.x, v.x );\n\t\t\tthis.y = Math.max( this.y, v.y );\n\t\t\tthis.z = Math.max( this.z, v.z );\n\t\t\tthis.w = Math.max( this.w, v.w );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclamp: function ( min, max ) {\n\n\t\t\t// This function assumes min < max, if this assumption isn't true it will not operate correctly\n\n\t\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\t\t\tthis.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\t\t\tthis.w = Math.max( min.w, Math.min( max.w, this.w ) );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclampScalar: function () {\n\n\t\t\tvar min, max;\n\n\t\t\treturn function clampScalar( minVal, maxVal ) {\n\n\t\t\t\tif ( min === undefined ) {\n\n\t\t\t\t\tmin = new Vector4();\n\t\t\t\t\tmax = new Vector4();\n\n\t\t\t\t}\n\n\t\t\t\tmin.set( minVal, minVal, minVal, minVal );\n\t\t\t\tmax.set( maxVal, maxVal, maxVal, maxVal );\n\n\t\t\t\treturn this.clamp( min, max );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tfloor: function () {\n\n\t\t\tthis.x = Math.floor( this.x );\n\t\t\tthis.y = Math.floor( this.y );\n\t\t\tthis.z = Math.floor( this.z );\n\t\t\tthis.w = Math.floor( this.w );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tceil: function () {\n\n\t\t\tthis.x = Math.ceil( this.x );\n\t\t\tthis.y = Math.ceil( this.y );\n\t\t\tthis.z = Math.ceil( this.z );\n\t\t\tthis.w = Math.ceil( this.w );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tround: function () {\n\n\t\t\tthis.x = Math.round( this.x );\n\t\t\tthis.y = Math.round( this.y );\n\t\t\tthis.z = Math.round( this.z );\n\t\t\tthis.w = Math.round( this.w );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\troundToZero: function () {\n\n\t\t\tthis.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n\t\t\tthis.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n\t\t\tthis.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );\n\t\t\tthis.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tnegate: function () {\n\n\t\t\tthis.x = - this.x;\n\t\t\tthis.y = - this.y;\n\t\t\tthis.z = - this.z;\n\t\t\tthis.w = - this.w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdot: function ( v ) {\n\n\t\t\treturn this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;\n\n\t\t},\n\n\t\tlengthSq: function () {\n\n\t\t\treturn this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;\n\n\t\t},\n\n\t\tlength: function () {\n\n\t\t\treturn Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );\n\n\t\t},\n\n\t\tlengthManhattan: function () {\n\n\t\t\treturn Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );\n\n\t\t},\n\n\t\tnormalize: function () {\n\n\t\t\treturn this.divideScalar( this.length() );\n\n\t\t},\n\n\t\tsetLength: function ( length ) {\n\n\t\t\treturn this.multiplyScalar( length / this.length() );\n\n\t\t},\n\n\t\tlerp: function ( v, alpha ) {\n\n\t\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\t\tthis.y += ( v.y - this.y ) * alpha;\n\t\t\tthis.z += ( v.z - this.z ) * alpha;\n\t\t\tthis.w += ( v.w - this.w ) * alpha;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tlerpVectors: function ( v1, v2, alpha ) {\n\n\t\t\treturn this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n\t\t},\n\n\t\tequals: function ( v ) {\n\n\t\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );\n\n\t\t},\n\n\t\tfromArray: function ( array, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tthis.x = array[ offset ];\n\t\t\tthis.y = array[ offset + 1 ];\n\t\t\tthis.z = array[ offset + 2 ];\n\t\t\tthis.w = array[ offset + 3 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tarray[ offset ] = this.x;\n\t\t\tarray[ offset + 1 ] = this.y;\n\t\t\tarray[ offset + 2 ] = this.z;\n\t\t\tarray[ offset + 3 ] = this.w;\n\n\t\t\treturn array;\n\n\t\t},\n\n\t\tfromAttribute: function ( attribute, index, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tindex = index * attribute.itemSize + offset;\n\n\t\t\tthis.x = attribute.array[ index ];\n\t\t\tthis.y = attribute.array[ index + 1 ];\n\t\t\tthis.z = attribute.array[ index + 2 ];\n\t\t\tthis.w = attribute.array[ index + 3 ];\n\n\t\t\treturn this;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author szimek / https://github.com/szimek/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author Marius Kintel / https://github.com/kintel\n\t */\n\n\t/*\n\t In options, we can specify:\n\t * Texture parameters for an auto-generated target texture\n\t * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers\n\t*/\n\tfunction WebGLRenderTarget( width, height, options ) {\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\n\t\tthis.scissor = new Vector4( 0, 0, width, height );\n\t\tthis.scissorTest = false;\n\n\t\tthis.viewport = new Vector4( 0, 0, width, height );\n\n\t\toptions = options || {};\n\n\t\tif ( options.minFilter === undefined ) options.minFilter = LinearFilter;\n\n\t\tthis.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );\n\n\t\tthis.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;\n\t\tthis.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;\n\t\tthis.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;\n\n\t}\n\n\tObject.assign( WebGLRenderTarget.prototype, EventDispatcher.prototype, {\n\n\t\tisWebGLRenderTarget: true,\n\n\t\tsetSize: function ( width, height ) {\n\n\t\t\tif ( this.width !== width || this.height !== height ) {\n\n\t\t\t\tthis.width = width;\n\t\t\t\tthis.height = height;\n\n\t\t\t\tthis.dispose();\n\n\t\t\t}\n\n\t\t\tthis.viewport.set( 0, 0, width, height );\n\t\t\tthis.scissor.set( 0, 0, width, height );\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tthis.width = source.width;\n\t\t\tthis.height = source.height;\n\n\t\t\tthis.viewport.copy( source.viewport );\n\n\t\t\tthis.texture = source.texture.clone();\n\n\t\t\tthis.depthBuffer = source.depthBuffer;\n\t\t\tthis.stencilBuffer = source.stencilBuffer;\n\t\t\tthis.depthTexture = source.depthTexture;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdispose: function () {\n\n\t\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com\n\t */\n\n\tfunction WebGLRenderTargetCube( width, height, options ) {\n\n\t\tWebGLRenderTarget.call( this, width, height, options );\n\n\t\tthis.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5\n\t\tthis.activeMipMapLevel = 0;\n\n\t}\n\n\tWebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype );\n\tWebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;\n\n\tWebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author WestLangley / http://github.com/WestLangley\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Quaternion( x, y, z, w ) {\n\n\t\tthis._x = x || 0;\n\t\tthis._y = y || 0;\n\t\tthis._z = z || 0;\n\t\tthis._w = ( w !== undefined ) ? w : 1;\n\n\t}\n\n\tQuaternion.prototype = {\n\n\t\tconstructor: Quaternion,\n\n\t\tget x () {\n\n\t\t\treturn this._x;\n\n\t\t},\n\n\t\tset x ( value ) {\n\n\t\t\tthis._x = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tget y () {\n\n\t\t\treturn this._y;\n\n\t\t},\n\n\t\tset y ( value ) {\n\n\t\t\tthis._y = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tget z () {\n\n\t\t\treturn this._z;\n\n\t\t},\n\n\t\tset z ( value ) {\n\n\t\t\tthis._z = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tget w () {\n\n\t\t\treturn this._w;\n\n\t\t},\n\n\t\tset w ( value ) {\n\n\t\t\tthis._w = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tset: function ( x, y, z, w ) {\n\n\t\t\tthis._x = x;\n\t\t\tthis._y = y;\n\t\t\tthis._z = z;\n\t\t\tthis._w = w;\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this._x, this._y, this._z, this._w );\n\n\t\t},\n\n\t\tcopy: function ( quaternion ) {\n\n\t\t\tthis._x = quaternion.x;\n\t\t\tthis._y = quaternion.y;\n\t\t\tthis._z = quaternion.z;\n\t\t\tthis._w = quaternion.w;\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromEuler: function ( euler, update ) {\n\n\t\t\tif ( (euler && euler.isEuler) === false ) {\n\n\t\t\t\tthrow new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );\n\n\t\t\t}\n\n\t\t\t// http://www.mathworks.com/matlabcentral/fileexchange/\n\t\t\t// \t20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/\n\t\t\t//\tcontent/SpinCalc.m\n\n\t\t\tvar c1 = Math.cos( euler._x / 2 );\n\t\t\tvar c2 = Math.cos( euler._y / 2 );\n\t\t\tvar c3 = Math.cos( euler._z / 2 );\n\t\t\tvar s1 = Math.sin( euler._x / 2 );\n\t\t\tvar s2 = Math.sin( euler._y / 2 );\n\t\t\tvar s3 = Math.sin( euler._z / 2 );\n\n\t\t\tvar order = euler.order;\n\n\t\t\tif ( order === 'XYZ' ) {\n\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n\t\t\t} else if ( order === 'YXZ' ) {\n\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n\t\t\t} else if ( order === 'ZXY' ) {\n\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n\t\t\t} else if ( order === 'ZYX' ) {\n\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n\t\t\t} else if ( order === 'YZX' ) {\n\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n\t\t\t} else if ( order === 'XZY' ) {\n\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n\t\t\t}\n\n\t\t\tif ( update !== false ) this.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromAxisAngle: function ( axis, angle ) {\n\n\t\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm\n\n\t\t\t// assumes axis is normalized\n\n\t\t\tvar halfAngle = angle / 2, s = Math.sin( halfAngle );\n\n\t\t\tthis._x = axis.x * s;\n\t\t\tthis._y = axis.y * s;\n\t\t\tthis._z = axis.z * s;\n\t\t\tthis._w = Math.cos( halfAngle );\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromRotationMatrix: function ( m ) {\n\n\t\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n\n\t\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\t\tvar te = m.elements,\n\n\t\t\t\tm11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n\t\t\t\tm21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n\t\t\t\tm31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],\n\n\t\t\t\ttrace = m11 + m22 + m33,\n\t\t\t\ts;\n\n\t\t\tif ( trace > 0 ) {\n\n\t\t\t\ts = 0.5 / Math.sqrt( trace + 1.0 );\n\n\t\t\t\tthis._w = 0.25 / s;\n\t\t\t\tthis._x = ( m32 - m23 ) * s;\n\t\t\t\tthis._y = ( m13 - m31 ) * s;\n\t\t\t\tthis._z = ( m21 - m12 ) * s;\n\n\t\t\t} else if ( m11 > m22 && m11 > m33 ) {\n\n\t\t\t\ts = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );\n\n\t\t\t\tthis._w = ( m32 - m23 ) / s;\n\t\t\t\tthis._x = 0.25 * s;\n\t\t\t\tthis._y = ( m12 + m21 ) / s;\n\t\t\t\tthis._z = ( m13 + m31 ) / s;\n\n\t\t\t} else if ( m22 > m33 ) {\n\n\t\t\t\ts = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );\n\n\t\t\t\tthis._w = ( m13 - m31 ) / s;\n\t\t\t\tthis._x = ( m12 + m21 ) / s;\n\t\t\t\tthis._y = 0.25 * s;\n\t\t\t\tthis._z = ( m23 + m32 ) / s;\n\n\t\t\t} else {\n\n\t\t\t\ts = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );\n\n\t\t\t\tthis._w = ( m21 - m12 ) / s;\n\t\t\t\tthis._x = ( m13 + m31 ) / s;\n\t\t\t\tthis._y = ( m23 + m32 ) / s;\n\t\t\t\tthis._z = 0.25 * s;\n\n\t\t\t}\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromUnitVectors: function () {\n\n\t\t\t// http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final\n\n\t\t\t// assumes direction vectors vFrom and vTo are normalized\n\n\t\t\tvar v1, r;\n\n\t\t\tvar EPS = 0.000001;\n\n\t\t\treturn function setFromUnitVectors( vFrom, vTo ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\n\t\t\t\tr = vFrom.dot( vTo ) + 1;\n\n\t\t\t\tif ( r < EPS ) {\n\n\t\t\t\t\tr = 0;\n\n\t\t\t\t\tif ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {\n\n\t\t\t\t\t\tv1.set( - vFrom.y, vFrom.x, 0 );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tv1.set( 0, - vFrom.z, vFrom.y );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tv1.crossVectors( vFrom, vTo );\n\n\t\t\t\t}\n\n\t\t\t\tthis._x = v1.x;\n\t\t\t\tthis._y = v1.y;\n\t\t\t\tthis._z = v1.z;\n\t\t\t\tthis._w = r;\n\n\t\t\t\treturn this.normalize();\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tinverse: function () {\n\n\t\t\treturn this.conjugate().normalize();\n\n\t\t},\n\n\t\tconjugate: function () {\n\n\t\t\tthis._x *= - 1;\n\t\t\tthis._y *= - 1;\n\t\t\tthis._z *= - 1;\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdot: function ( v ) {\n\n\t\t\treturn this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;\n\n\t\t},\n\n\t\tlengthSq: function () {\n\n\t\t\treturn this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;\n\n\t\t},\n\n\t\tlength: function () {\n\n\t\t\treturn Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );\n\n\t\t},\n\n\t\tnormalize: function () {\n\n\t\t\tvar l = this.length();\n\n\t\t\tif ( l === 0 ) {\n\n\t\t\t\tthis._x = 0;\n\t\t\t\tthis._y = 0;\n\t\t\t\tthis._z = 0;\n\t\t\t\tthis._w = 1;\n\n\t\t\t} else {\n\n\t\t\t\tl = 1 / l;\n\n\t\t\t\tthis._x = this._x * l;\n\t\t\t\tthis._y = this._y * l;\n\t\t\t\tthis._z = this._z * l;\n\t\t\t\tthis._w = this._w * l;\n\n\t\t\t}\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiply: function ( q, p ) {\n\n\t\t\tif ( p !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );\n\t\t\t\treturn this.multiplyQuaternions( q, p );\n\n\t\t\t}\n\n\t\t\treturn this.multiplyQuaternions( this, q );\n\n\t\t},\n\n\t\tpremultiply: function ( q ) {\n\n\t\t\treturn this.multiplyQuaternions( q, this );\n\n\t\t},\n\n\t\tmultiplyQuaternions: function ( a, b ) {\n\n\t\t\t// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm\n\n\t\t\tvar qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;\n\t\t\tvar qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;\n\n\t\t\tthis._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;\n\t\t\tthis._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;\n\t\t\tthis._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;\n\t\t\tthis._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tslerp: function ( qb, t ) {\n\n\t\t\tif ( t === 0 ) return this;\n\t\t\tif ( t === 1 ) return this.copy( qb );\n\n\t\t\tvar x = this._x, y = this._y, z = this._z, w = this._w;\n\n\t\t\t// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/\n\n\t\t\tvar cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;\n\n\t\t\tif ( cosHalfTheta < 0 ) {\n\n\t\t\t\tthis._w = - qb._w;\n\t\t\t\tthis._x = - qb._x;\n\t\t\t\tthis._y = - qb._y;\n\t\t\t\tthis._z = - qb._z;\n\n\t\t\t\tcosHalfTheta = - cosHalfTheta;\n\n\t\t\t} else {\n\n\t\t\t\tthis.copy( qb );\n\n\t\t\t}\n\n\t\t\tif ( cosHalfTheta >= 1.0 ) {\n\n\t\t\t\tthis._w = w;\n\t\t\t\tthis._x = x;\n\t\t\t\tthis._y = y;\n\t\t\t\tthis._z = z;\n\n\t\t\t\treturn this;\n\n\t\t\t}\n\n\t\t\tvar sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );\n\n\t\t\tif ( Math.abs( sinHalfTheta ) < 0.001 ) {\n\n\t\t\t\tthis._w = 0.5 * ( w + this._w );\n\t\t\t\tthis._x = 0.5 * ( x + this._x );\n\t\t\t\tthis._y = 0.5 * ( y + this._y );\n\t\t\t\tthis._z = 0.5 * ( z + this._z );\n\n\t\t\t\treturn this;\n\n\t\t\t}\n\n\t\t\tvar halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );\n\t\t\tvar ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,\n\t\t\tratioB = Math.sin( t * halfTheta ) / sinHalfTheta;\n\n\t\t\tthis._w = ( w * ratioA + this._w * ratioB );\n\t\t\tthis._x = ( x * ratioA + this._x * ratioB );\n\t\t\tthis._y = ( y * ratioA + this._y * ratioB );\n\t\t\tthis._z = ( z * ratioA + this._z * ratioB );\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( quaternion ) {\n\n\t\t\treturn ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );\n\n\t\t},\n\n\t\tfromArray: function ( array, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tthis._x = array[ offset ];\n\t\t\tthis._y = array[ offset + 1 ];\n\t\t\tthis._z = array[ offset + 2 ];\n\t\t\tthis._w = array[ offset + 3 ];\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tarray[ offset ] = this._x;\n\t\t\tarray[ offset + 1 ] = this._y;\n\t\t\tarray[ offset + 2 ] = this._z;\n\t\t\tarray[ offset + 3 ] = this._w;\n\n\t\t\treturn array;\n\n\t\t},\n\n\t\tonChange: function ( callback ) {\n\n\t\t\tthis.onChangeCallback = callback;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tonChangeCallback: function () {}\n\n\t};\n\n\tObject.assign( Quaternion, {\n\n\t\tslerp: function( qa, qb, qm, t ) {\n\n\t\t\treturn qm.copy( qa ).slerp( qb, t );\n\n\t\t},\n\n\t\tslerpFlat: function(\n\t\t\t\tdst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {\n\n\t\t\t// fuzz-free, array-based Quaternion SLERP operation\n\n\t\t\tvar x0 = src0[ srcOffset0 + 0 ],\n\t\t\t\ty0 = src0[ srcOffset0 + 1 ],\n\t\t\t\tz0 = src0[ srcOffset0 + 2 ],\n\t\t\t\tw0 = src0[ srcOffset0 + 3 ],\n\n\t\t\t\tx1 = src1[ srcOffset1 + 0 ],\n\t\t\t\ty1 = src1[ srcOffset1 + 1 ],\n\t\t\t\tz1 = src1[ srcOffset1 + 2 ],\n\t\t\t\tw1 = src1[ srcOffset1 + 3 ];\n\n\t\t\tif ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {\n\n\t\t\t\tvar s = 1 - t,\n\n\t\t\t\t\tcos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,\n\n\t\t\t\t\tdir = ( cos >= 0 ? 1 : - 1 ),\n\t\t\t\t\tsqrSin = 1 - cos * cos;\n\n\t\t\t\t// Skip the Slerp for tiny steps to avoid numeric problems:\n\t\t\t\tif ( sqrSin > Number.EPSILON ) {\n\n\t\t\t\t\tvar sin = Math.sqrt( sqrSin ),\n\t\t\t\t\t\tlen = Math.atan2( sin, cos * dir );\n\n\t\t\t\t\ts = Math.sin( s * len ) / sin;\n\t\t\t\t\tt = Math.sin( t * len ) / sin;\n\n\t\t\t\t}\n\n\t\t\t\tvar tDir = t * dir;\n\n\t\t\t\tx0 = x0 * s + x1 * tDir;\n\t\t\t\ty0 = y0 * s + y1 * tDir;\n\t\t\t\tz0 = z0 * s + z1 * tDir;\n\t\t\t\tw0 = w0 * s + w1 * tDir;\n\n\t\t\t\t// Normalize in case we just did a lerp:\n\t\t\t\tif ( s === 1 - t ) {\n\n\t\t\t\t\tvar f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );\n\n\t\t\t\t\tx0 *= f;\n\t\t\t\t\ty0 *= f;\n\t\t\t\t\tz0 *= f;\n\t\t\t\t\tw0 *= f;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tdst[ dstOffset ] = x0;\n\t\t\tdst[ dstOffset + 1 ] = y0;\n\t\t\tdst[ dstOffset + 2 ] = z0;\n\t\t\tdst[ dstOffset + 3 ] = w0;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author *kile / http://kile.stravaganza.org/\n\t * @author philogb / http://blog.thejit.org/\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author egraether / http://egraether.com/\n\t * @author WestLangley / http://github.com/WestLangley\n\t */\n\n\tfunction Vector3( x, y, z ) {\n\n\t\tthis.x = x || 0;\n\t\tthis.y = y || 0;\n\t\tthis.z = z || 0;\n\n\t}\n\n\tVector3.prototype = {\n\n\t\tconstructor: Vector3,\n\n\t\tisVector3: true,\n\n\t\tset: function ( x, y, z ) {\n\n\t\t\tthis.x = x;\n\t\t\tthis.y = y;\n\t\t\tthis.z = z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetScalar: function ( scalar ) {\n\n\t\t\tthis.x = scalar;\n\t\t\tthis.y = scalar;\n\t\t\tthis.z = scalar;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetX: function ( x ) {\n\n\t\t\tthis.x = x;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetY: function ( y ) {\n\n\t\t\tthis.y = y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetZ: function ( z ) {\n\n\t\t\tthis.z = z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetComponent: function ( index, value ) {\n\n\t\t\tswitch ( index ) {\n\n\t\t\t\tcase 0: this.x = value; break;\n\t\t\t\tcase 1: this.y = value; break;\n\t\t\t\tcase 2: this.z = value; break;\n\t\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t\t}\n\t\t\t\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetComponent: function ( index ) {\n\n\t\t\tswitch ( index ) {\n\n\t\t\t\tcase 0: return this.x;\n\t\t\t\tcase 1: return this.y;\n\t\t\t\tcase 2: return this.z;\n\t\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t\t}\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.x, this.y, this.z );\n\n\t\t},\n\n\t\tcopy: function ( v ) {\n\n\t\t\tthis.x = v.x;\n\t\t\tthis.y = v.y;\n\t\t\tthis.z = v.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tadd: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n\t\t\t\treturn this.addVectors( v, w );\n\n\t\t\t}\n\n\t\t\tthis.x += v.x;\n\t\t\tthis.y += v.y;\n\t\t\tthis.z += v.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddScalar: function ( s ) {\n\n\t\t\tthis.x += s;\n\t\t\tthis.y += s;\n\t\t\tthis.z += s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddVectors: function ( a, b ) {\n\n\t\t\tthis.x = a.x + b.x;\n\t\t\tthis.y = a.y + b.y;\n\t\t\tthis.z = a.z + b.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddScaledVector: function ( v, s ) {\n\n\t\t\tthis.x += v.x * s;\n\t\t\tthis.y += v.y * s;\n\t\t\tthis.z += v.z * s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsub: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n\t\t\t\treturn this.subVectors( v, w );\n\n\t\t\t}\n\n\t\t\tthis.x -= v.x;\n\t\t\tthis.y -= v.y;\n\t\t\tthis.z -= v.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsubScalar: function ( s ) {\n\n\t\t\tthis.x -= s;\n\t\t\tthis.y -= s;\n\t\t\tthis.z -= s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsubVectors: function ( a, b ) {\n\n\t\t\tthis.x = a.x - b.x;\n\t\t\tthis.y = a.y - b.y;\n\t\t\tthis.z = a.z - b.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiply: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );\n\t\t\t\treturn this.multiplyVectors( v, w );\n\n\t\t\t}\n\n\t\t\tthis.x *= v.x;\n\t\t\tthis.y *= v.y;\n\t\t\tthis.z *= v.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiplyScalar: function ( scalar ) {\n\n\t\t\tif ( isFinite( scalar ) ) {\n\n\t\t\t\tthis.x *= scalar;\n\t\t\t\tthis.y *= scalar;\n\t\t\t\tthis.z *= scalar;\n\n\t\t\t} else {\n\n\t\t\t\tthis.x = 0;\n\t\t\t\tthis.y = 0;\n\t\t\t\tthis.z = 0;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiplyVectors: function ( a, b ) {\n\n\t\t\tthis.x = a.x * b.x;\n\t\t\tthis.y = a.y * b.y;\n\t\t\tthis.z = a.z * b.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyEuler: function () {\n\n\t\t\tvar quaternion;\n\n\t\t\treturn function applyEuler( euler ) {\n\n\t\t\t\tif ( (euler && euler.isEuler) === false ) {\n\n\t\t\t\t\tconsole.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );\n\n\t\t\t\t}\n\n\t\t\t\tif ( quaternion === undefined ) quaternion = new Quaternion();\n\n\t\t\t\treturn this.applyQuaternion( quaternion.setFromEuler( euler ) );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tapplyAxisAngle: function () {\n\n\t\t\tvar quaternion;\n\n\t\t\treturn function applyAxisAngle( axis, angle ) {\n\n\t\t\t\tif ( quaternion === undefined ) quaternion = new Quaternion();\n\n\t\t\t\treturn this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tapplyMatrix3: function ( m ) {\n\n\t\t\tvar x = this.x, y = this.y, z = this.z;\n\t\t\tvar e = m.elements;\n\n\t\t\tthis.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;\n\t\t\tthis.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;\n\t\t\tthis.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyMatrix4: function ( m ) {\n\n\t\t\t// input: THREE.Matrix4 affine matrix\n\n\t\t\tvar x = this.x, y = this.y, z = this.z;\n\t\t\tvar e = m.elements;\n\n\t\t\tthis.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];\n\t\t\tthis.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];\n\t\t\tthis.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyProjection: function ( m ) {\n\n\t\t\t// input: THREE.Matrix4 projection matrix\n\n\t\t\tvar x = this.x, y = this.y, z = this.z;\n\t\t\tvar e = m.elements;\n\t\t\tvar d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide\n\n\t\t\tthis.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;\n\t\t\tthis.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;\n\t\t\tthis.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyQuaternion: function ( q ) {\n\n\t\t\tvar x = this.x, y = this.y, z = this.z;\n\t\t\tvar qx = q.x, qy = q.y, qz = q.z, qw = q.w;\n\n\t\t\t// calculate quat * vector\n\n\t\t\tvar ix = qw * x + qy * z - qz * y;\n\t\t\tvar iy = qw * y + qz * x - qx * z;\n\t\t\tvar iz = qw * z + qx * y - qy * x;\n\t\t\tvar iw = - qx * x - qy * y - qz * z;\n\n\t\t\t// calculate result * inverse quat\n\n\t\t\tthis.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;\n\t\t\tthis.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;\n\t\t\tthis.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tproject: function () {\n\n\t\t\tvar matrix;\n\n\t\t\treturn function project( camera ) {\n\n\t\t\t\tif ( matrix === undefined ) matrix = new Matrix4();\n\n\t\t\t\tmatrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );\n\t\t\t\treturn this.applyProjection( matrix );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tunproject: function () {\n\n\t\t\tvar matrix;\n\n\t\t\treturn function unproject( camera ) {\n\n\t\t\t\tif ( matrix === undefined ) matrix = new Matrix4();\n\n\t\t\t\tmatrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );\n\t\t\t\treturn this.applyProjection( matrix );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttransformDirection: function ( m ) {\n\n\t\t\t// input: THREE.Matrix4 affine matrix\n\t\t\t// vector interpreted as a direction\n\n\t\t\tvar x = this.x, y = this.y, z = this.z;\n\t\t\tvar e = m.elements;\n\n\t\t\tthis.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;\n\t\t\tthis.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;\n\t\t\tthis.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;\n\n\t\t\treturn this.normalize();\n\n\t\t},\n\n\t\tdivide: function ( v ) {\n\n\t\t\tthis.x /= v.x;\n\t\t\tthis.y /= v.y;\n\t\t\tthis.z /= v.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdivideScalar: function ( scalar ) {\n\n\t\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t\t},\n\n\t\tmin: function ( v ) {\n\n\t\t\tthis.x = Math.min( this.x, v.x );\n\t\t\tthis.y = Math.min( this.y, v.y );\n\t\t\tthis.z = Math.min( this.z, v.z );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmax: function ( v ) {\n\n\t\t\tthis.x = Math.max( this.x, v.x );\n\t\t\tthis.y = Math.max( this.y, v.y );\n\t\t\tthis.z = Math.max( this.z, v.z );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclamp: function ( min, max ) {\n\n\t\t\t// This function assumes min < max, if this assumption isn't true it will not operate correctly\n\n\t\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\t\t\tthis.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclampScalar: function () {\n\n\t\t\tvar min, max;\n\n\t\t\treturn function clampScalar( minVal, maxVal ) {\n\n\t\t\t\tif ( min === undefined ) {\n\n\t\t\t\t\tmin = new Vector3();\n\t\t\t\t\tmax = new Vector3();\n\n\t\t\t\t}\n\n\t\t\t\tmin.set( minVal, minVal, minVal );\n\t\t\t\tmax.set( maxVal, maxVal, maxVal );\n\n\t\t\t\treturn this.clamp( min, max );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclampLength: function ( min, max ) {\n\n\t\t\tvar length = this.length();\n\n\t\t\treturn this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );\n\n\t\t},\n\n\t\tfloor: function () {\n\n\t\t\tthis.x = Math.floor( this.x );\n\t\t\tthis.y = Math.floor( this.y );\n\t\t\tthis.z = Math.floor( this.z );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tceil: function () {\n\n\t\t\tthis.x = Math.ceil( this.x );\n\t\t\tthis.y = Math.ceil( this.y );\n\t\t\tthis.z = Math.ceil( this.z );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tround: function () {\n\n\t\t\tthis.x = Math.round( this.x );\n\t\t\tthis.y = Math.round( this.y );\n\t\t\tthis.z = Math.round( this.z );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\troundToZero: function () {\n\n\t\t\tthis.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n\t\t\tthis.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n\t\t\tthis.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tnegate: function () {\n\n\t\t\tthis.x = - this.x;\n\t\t\tthis.y = - this.y;\n\t\t\tthis.z = - this.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdot: function ( v ) {\n\n\t\t\treturn this.x * v.x + this.y * v.y + this.z * v.z;\n\n\t\t},\n\n\t\tlengthSq: function () {\n\n\t\t\treturn this.x * this.x + this.y * this.y + this.z * this.z;\n\n\t\t},\n\n\t\tlength: function () {\n\n\t\t\treturn Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );\n\n\t\t},\n\n\t\tlengthManhattan: function () {\n\n\t\t\treturn Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );\n\n\t\t},\n\n\t\tnormalize: function () {\n\n\t\t\treturn this.divideScalar( this.length() );\n\n\t\t},\n\n\t\tsetLength: function ( length ) {\n\n\t\t\treturn this.multiplyScalar( length / this.length() );\n\n\t\t},\n\n\t\tlerp: function ( v, alpha ) {\n\n\t\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\t\tthis.y += ( v.y - this.y ) * alpha;\n\t\t\tthis.z += ( v.z - this.z ) * alpha;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tlerpVectors: function ( v1, v2, alpha ) {\n\n\t\t\treturn this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n\t\t},\n\n\t\tcross: function ( v, w ) {\n\n\t\t\tif ( w !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );\n\t\t\t\treturn this.crossVectors( v, w );\n\n\t\t\t}\n\n\t\t\tvar x = this.x, y = this.y, z = this.z;\n\n\t\t\tthis.x = y * v.z - z * v.y;\n\t\t\tthis.y = z * v.x - x * v.z;\n\t\t\tthis.z = x * v.y - y * v.x;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcrossVectors: function ( a, b ) {\n\n\t\t\tvar ax = a.x, ay = a.y, az = a.z;\n\t\t\tvar bx = b.x, by = b.y, bz = b.z;\n\n\t\t\tthis.x = ay * bz - az * by;\n\t\t\tthis.y = az * bx - ax * bz;\n\t\t\tthis.z = ax * by - ay * bx;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tprojectOnVector: function ( vector ) {\n\n\t\t\tvar scalar = vector.dot( this ) / vector.lengthSq();\n\n\t\t\treturn this.copy( vector ).multiplyScalar( scalar );\n\n\t\t},\n\n\t\tprojectOnPlane: function () {\n\n\t\t\tvar v1;\n\n\t\t\treturn function projectOnPlane( planeNormal ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\n\t\t\t\tv1.copy( this ).projectOnVector( planeNormal );\n\n\t\t\t\treturn this.sub( v1 );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\treflect: function () {\n\n\t\t\t// reflect incident vector off plane orthogonal to normal\n\t\t\t// normal is assumed to have unit length\n\n\t\t\tvar v1;\n\n\t\t\treturn function reflect( normal ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\n\t\t\t\treturn this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tangleTo: function ( v ) {\n\n\t\t\tvar theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );\n\n\t\t\t// clamp, to handle numerical problems\n\n\t\t\treturn Math.acos( _Math.clamp( theta, - 1, 1 ) );\n\n\t\t},\n\n\t\tdistanceTo: function ( v ) {\n\n\t\t\treturn Math.sqrt( this.distanceToSquared( v ) );\n\n\t\t},\n\n\t\tdistanceToSquared: function ( v ) {\n\n\t\t\tvar dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;\n\n\t\t\treturn dx * dx + dy * dy + dz * dz;\n\n\t\t},\n\n\t\tdistanceToManhattan: function ( v ) {\n\n\t\t\treturn Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );\n\n\t\t},\n\n\t\tsetFromSpherical: function( s ) {\n\n\t\t\tvar sinPhiRadius = Math.sin( s.phi ) * s.radius;\n\n\t\t\tthis.x = sinPhiRadius * Math.sin( s.theta );\n\t\t\tthis.y = Math.cos( s.phi ) * s.radius;\n\t\t\tthis.z = sinPhiRadius * Math.cos( s.theta );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromMatrixPosition: function ( m ) {\n\n\t\t\treturn this.setFromMatrixColumn( m, 3 );\n\n\t\t},\n\n\t\tsetFromMatrixScale: function ( m ) {\n\n\t\t\tvar sx = this.setFromMatrixColumn( m, 0 ).length();\n\t\t\tvar sy = this.setFromMatrixColumn( m, 1 ).length();\n\t\t\tvar sz = this.setFromMatrixColumn( m, 2 ).length();\n\n\t\t\tthis.x = sx;\n\t\t\tthis.y = sy;\n\t\t\tthis.z = sz;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromMatrixColumn: function ( m, index ) {\n\n\t\t\tif ( typeof m === 'number' ) {\n\n\t\t\t\tconsole.warn( 'THREE.Vector3: setFromMatrixColumn now expects ( matrix, index ).' );\n\t\t\t\tvar temp = m;\n\t\t\t\tm = index;\n\t\t\t\tindex = temp;\n\n\t\t\t}\n\n\t\t\treturn this.fromArray( m.elements, index * 4 );\n\n\t\t},\n\n\t\tequals: function ( v ) {\n\n\t\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );\n\n\t\t},\n\n\t\tfromArray: function ( array, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tthis.x = array[ offset ];\n\t\t\tthis.y = array[ offset + 1 ];\n\t\t\tthis.z = array[ offset + 2 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tarray[ offset ] = this.x;\n\t\t\tarray[ offset + 1 ] = this.y;\n\t\t\tarray[ offset + 2 ] = this.z;\n\n\t\t\treturn array;\n\n\t\t},\n\n\t\tfromAttribute: function ( attribute, index, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tindex = index * attribute.itemSize + offset;\n\n\t\t\tthis.x = attribute.array[ index ];\n\t\t\tthis.y = attribute.array[ index + 1 ];\n\t\t\tthis.z = attribute.array[ index + 2 ];\n\n\t\t\treturn this;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author supereggbert / http://www.paulbrunt.co.uk/\n\t * @author philogb / http://blog.thejit.org/\n\t * @author jordi_ros / http://plattsoft.com\n\t * @author D1plo1d / http://github.com/D1plo1d\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author timknip / http://www.floorplanner.com/\n\t * @author bhouston / http://clara.io\n\t * @author WestLangley / http://github.com/WestLangley\n\t */\n\n\tfunction Matrix4() {\n\n\t\tthis.elements = new Float32Array( [\n\n\t\t\t1, 0, 0, 0,\n\t\t\t0, 1, 0, 0,\n\t\t\t0, 0, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t] );\n\n\t\tif ( arguments.length > 0 ) {\n\n\t\t\tconsole.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );\n\n\t\t}\n\n\t}\n\n\tMatrix4.prototype = {\n\n\t\tconstructor: Matrix4,\n\n\t\tisMatrix4: true,\n\n\t\tset: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tte[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;\n\t\t\tte[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;\n\t\t\tte[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;\n\t\t\tte[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tidentity: function () {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0, 0,\n\t\t\t\t0, 1, 0, 0,\n\t\t\t\t0, 0, 1, 0,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new Matrix4().fromArray( this.elements );\n\n\t\t},\n\n\t\tcopy: function ( m ) {\n\n\t\t\tthis.elements.set( m.elements );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyPosition: function ( m ) {\n\n\t\t\tvar te = this.elements;\n\t\t\tvar me = m.elements;\n\n\t\t\tte[ 12 ] = me[ 12 ];\n\t\t\tte[ 13 ] = me[ 13 ];\n\t\t\tte[ 14 ] = me[ 14 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\textractBasis: function ( xAxis, yAxis, zAxis ) {\n\n\t\t\txAxis.setFromMatrixColumn( this, 0 );\n\t\t\tyAxis.setFromMatrixColumn( this, 1 );\n\t\t\tzAxis.setFromMatrixColumn( this, 2 );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeBasis: function ( xAxis, yAxis, zAxis ) {\n\n\t\t\tthis.set(\n\t\t\t\txAxis.x, yAxis.x, zAxis.x, 0,\n\t\t\t\txAxis.y, yAxis.y, zAxis.y, 0,\n\t\t\t\txAxis.z, yAxis.z, zAxis.z, 0,\n\t\t\t\t0, 0, 0, 1\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\textractRotation: function () {\n\n\t\t\tvar v1;\n\n\t\t\treturn function extractRotation( m ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\n\t\t\t\tvar te = this.elements;\n\t\t\t\tvar me = m.elements;\n\n\t\t\t\tvar scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();\n\t\t\t\tvar scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();\n\t\t\t\tvar scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();\n\n\t\t\t\tte[ 0 ] = me[ 0 ] * scaleX;\n\t\t\t\tte[ 1 ] = me[ 1 ] * scaleX;\n\t\t\t\tte[ 2 ] = me[ 2 ] * scaleX;\n\n\t\t\t\tte[ 4 ] = me[ 4 ] * scaleY;\n\t\t\t\tte[ 5 ] = me[ 5 ] * scaleY;\n\t\t\t\tte[ 6 ] = me[ 6 ] * scaleY;\n\n\t\t\t\tte[ 8 ] = me[ 8 ] * scaleZ;\n\t\t\t\tte[ 9 ] = me[ 9 ] * scaleZ;\n\t\t\t\tte[ 10 ] = me[ 10 ] * scaleZ;\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tmakeRotationFromEuler: function ( euler ) {\n\n\t\t\tif ( (euler && euler.isEuler) === false ) {\n\n\t\t\t\tconsole.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );\n\n\t\t\t}\n\n\t\t\tvar te = this.elements;\n\n\t\t\tvar x = euler.x, y = euler.y, z = euler.z;\n\t\t\tvar a = Math.cos( x ), b = Math.sin( x );\n\t\t\tvar c = Math.cos( y ), d = Math.sin( y );\n\t\t\tvar e = Math.cos( z ), f = Math.sin( z );\n\n\t\t\tif ( euler.order === 'XYZ' ) {\n\n\t\t\t\tvar ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n\t\t\t\tte[ 0 ] = c * e;\n\t\t\t\tte[ 4 ] = - c * f;\n\t\t\t\tte[ 8 ] = d;\n\n\t\t\t\tte[ 1 ] = af + be * d;\n\t\t\t\tte[ 5 ] = ae - bf * d;\n\t\t\t\tte[ 9 ] = - b * c;\n\n\t\t\t\tte[ 2 ] = bf - ae * d;\n\t\t\t\tte[ 6 ] = be + af * d;\n\t\t\t\tte[ 10 ] = a * c;\n\n\t\t\t} else if ( euler.order === 'YXZ' ) {\n\n\t\t\t\tvar ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n\t\t\t\tte[ 0 ] = ce + df * b;\n\t\t\t\tte[ 4 ] = de * b - cf;\n\t\t\t\tte[ 8 ] = a * d;\n\n\t\t\t\tte[ 1 ] = a * f;\n\t\t\t\tte[ 5 ] = a * e;\n\t\t\t\tte[ 9 ] = - b;\n\n\t\t\t\tte[ 2 ] = cf * b - de;\n\t\t\t\tte[ 6 ] = df + ce * b;\n\t\t\t\tte[ 10 ] = a * c;\n\n\t\t\t} else if ( euler.order === 'ZXY' ) {\n\n\t\t\t\tvar ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n\t\t\t\tte[ 0 ] = ce - df * b;\n\t\t\t\tte[ 4 ] = - a * f;\n\t\t\t\tte[ 8 ] = de + cf * b;\n\n\t\t\t\tte[ 1 ] = cf + de * b;\n\t\t\t\tte[ 5 ] = a * e;\n\t\t\t\tte[ 9 ] = df - ce * b;\n\n\t\t\t\tte[ 2 ] = - a * d;\n\t\t\t\tte[ 6 ] = b;\n\t\t\t\tte[ 10 ] = a * c;\n\n\t\t\t} else if ( euler.order === 'ZYX' ) {\n\n\t\t\t\tvar ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n\t\t\t\tte[ 0 ] = c * e;\n\t\t\t\tte[ 4 ] = be * d - af;\n\t\t\t\tte[ 8 ] = ae * d + bf;\n\n\t\t\t\tte[ 1 ] = c * f;\n\t\t\t\tte[ 5 ] = bf * d + ae;\n\t\t\t\tte[ 9 ] = af * d - be;\n\n\t\t\t\tte[ 2 ] = - d;\n\t\t\t\tte[ 6 ] = b * c;\n\t\t\t\tte[ 10 ] = a * c;\n\n\t\t\t} else if ( euler.order === 'YZX' ) {\n\n\t\t\t\tvar ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n\t\t\t\tte[ 0 ] = c * e;\n\t\t\t\tte[ 4 ] = bd - ac * f;\n\t\t\t\tte[ 8 ] = bc * f + ad;\n\n\t\t\t\tte[ 1 ] = f;\n\t\t\t\tte[ 5 ] = a * e;\n\t\t\t\tte[ 9 ] = - b * e;\n\n\t\t\t\tte[ 2 ] = - d * e;\n\t\t\t\tte[ 6 ] = ad * f + bc;\n\t\t\t\tte[ 10 ] = ac - bd * f;\n\n\t\t\t} else if ( euler.order === 'XZY' ) {\n\n\t\t\t\tvar ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n\t\t\t\tte[ 0 ] = c * e;\n\t\t\t\tte[ 4 ] = - f;\n\t\t\t\tte[ 8 ] = d * e;\n\n\t\t\t\tte[ 1 ] = ac * f + bd;\n\t\t\t\tte[ 5 ] = a * e;\n\t\t\t\tte[ 9 ] = ad * f - bc;\n\n\t\t\t\tte[ 2 ] = bc * f - ad;\n\t\t\t\tte[ 6 ] = b * e;\n\t\t\t\tte[ 10 ] = bd * f + ac;\n\n\t\t\t}\n\n\t\t\t// last column\n\t\t\tte[ 3 ] = 0;\n\t\t\tte[ 7 ] = 0;\n\t\t\tte[ 11 ] = 0;\n\n\t\t\t// bottom row\n\t\t\tte[ 12 ] = 0;\n\t\t\tte[ 13 ] = 0;\n\t\t\tte[ 14 ] = 0;\n\t\t\tte[ 15 ] = 1;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeRotationFromQuaternion: function ( q ) {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tvar x = q.x, y = q.y, z = q.z, w = q.w;\n\t\t\tvar x2 = x + x, y2 = y + y, z2 = z + z;\n\t\t\tvar xx = x * x2, xy = x * y2, xz = x * z2;\n\t\t\tvar yy = y * y2, yz = y * z2, zz = z * z2;\n\t\t\tvar wx = w * x2, wy = w * y2, wz = w * z2;\n\n\t\t\tte[ 0 ] = 1 - ( yy + zz );\n\t\t\tte[ 4 ] = xy - wz;\n\t\t\tte[ 8 ] = xz + wy;\n\n\t\t\tte[ 1 ] = xy + wz;\n\t\t\tte[ 5 ] = 1 - ( xx + zz );\n\t\t\tte[ 9 ] = yz - wx;\n\n\t\t\tte[ 2 ] = xz - wy;\n\t\t\tte[ 6 ] = yz + wx;\n\t\t\tte[ 10 ] = 1 - ( xx + yy );\n\n\t\t\t// last column\n\t\t\tte[ 3 ] = 0;\n\t\t\tte[ 7 ] = 0;\n\t\t\tte[ 11 ] = 0;\n\n\t\t\t// bottom row\n\t\t\tte[ 12 ] = 0;\n\t\t\tte[ 13 ] = 0;\n\t\t\tte[ 14 ] = 0;\n\t\t\tte[ 15 ] = 1;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tlookAt: function () {\n\n\t\t\tvar x, y, z;\n\n\t\t\treturn function lookAt( eye, target, up ) {\n\n\t\t\t\tif ( x === undefined ) {\n\n\t\t\t\t\tx = new Vector3();\n\t\t\t\t\ty = new Vector3();\n\t\t\t\t\tz = new Vector3();\n\n\t\t\t\t}\n\n\t\t\t\tvar te = this.elements;\n\n\t\t\t\tz.subVectors( eye, target ).normalize();\n\n\t\t\t\tif ( z.lengthSq() === 0 ) {\n\n\t\t\t\t\tz.z = 1;\n\n\t\t\t\t}\n\n\t\t\t\tx.crossVectors( up, z ).normalize();\n\n\t\t\t\tif ( x.lengthSq() === 0 ) {\n\n\t\t\t\t\tz.z += 0.0001;\n\t\t\t\t\tx.crossVectors( up, z ).normalize();\n\n\t\t\t\t}\n\n\t\t\t\ty.crossVectors( z, x );\n\n\n\t\t\t\tte[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;\n\t\t\t\tte[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;\n\t\t\t\tte[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tmultiply: function ( m, n ) {\n\n\t\t\tif ( n !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );\n\t\t\t\treturn this.multiplyMatrices( m, n );\n\n\t\t\t}\n\n\t\t\treturn this.multiplyMatrices( this, m );\n\n\t\t},\n\n\t\tpremultiply: function ( m ) {\n\n\t\t\treturn this.multiplyMatrices( m, this );\n\n\t\t},\n\n\t\tmultiplyMatrices: function ( a, b ) {\n\n\t\t\tvar ae = a.elements;\n\t\t\tvar be = b.elements;\n\t\t\tvar te = this.elements;\n\n\t\t\tvar a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];\n\t\t\tvar a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];\n\t\t\tvar a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];\n\t\t\tvar a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];\n\n\t\t\tvar b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];\n\t\t\tvar b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];\n\t\t\tvar b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];\n\t\t\tvar b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];\n\n\t\t\tte[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;\n\t\t\tte[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;\n\t\t\tte[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;\n\t\t\tte[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;\n\n\t\t\tte[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;\n\t\t\tte[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;\n\t\t\tte[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;\n\t\t\tte[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;\n\n\t\t\tte[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;\n\t\t\tte[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;\n\t\t\tte[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;\n\t\t\tte[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;\n\n\t\t\tte[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;\n\t\t\tte[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;\n\t\t\tte[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;\n\t\t\tte[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiplyToArray: function ( a, b, r ) {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tthis.multiplyMatrices( a, b );\n\n\t\t\tr[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ];\n\t\t\tr[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ];\n\t\t\tr[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ];\n\t\t\tr[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiplyScalar: function ( s ) {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tte[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;\n\t\t\tte[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;\n\t\t\tte[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;\n\t\t\tte[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyToVector3Array: function () {\n\n\t\t\tvar v1;\n\n\t\t\treturn function applyToVector3Array( array, offset, length ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\t\t\t\tif ( offset === undefined ) offset = 0;\n\t\t\t\tif ( length === undefined ) length = array.length;\n\n\t\t\t\tfor ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {\n\n\t\t\t\t\tv1.fromArray( array, j );\n\t\t\t\t\tv1.applyMatrix4( this );\n\t\t\t\t\tv1.toArray( array, j );\n\n\t\t\t\t}\n\n\t\t\t\treturn array;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tapplyToBuffer: function () {\n\n\t\t\tvar v1;\n\n\t\t\treturn function applyToBuffer( buffer, offset, length ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\t\t\t\tif ( offset === undefined ) offset = 0;\n\t\t\t\tif ( length === undefined ) length = buffer.length / buffer.itemSize;\n\n\t\t\t\tfor ( var i = 0, j = offset; i < length; i ++, j ++ ) {\n\n\t\t\t\t\tv1.x = buffer.getX( j );\n\t\t\t\t\tv1.y = buffer.getY( j );\n\t\t\t\t\tv1.z = buffer.getZ( j );\n\n\t\t\t\t\tv1.applyMatrix4( this );\n\n\t\t\t\t\tbuffer.setXYZ( j, v1.x, v1.y, v1.z );\n\n\t\t\t\t}\n\n\t\t\t\treturn buffer;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tdeterminant: function () {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tvar n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];\n\t\t\tvar n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];\n\t\t\tvar n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];\n\t\t\tvar n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];\n\n\t\t\t//TODO: make this more efficient\n\t\t\t//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )\n\n\t\t\treturn (\n\t\t\t\tn41 * (\n\t\t\t\t\t+ n14 * n23 * n32\n\t\t\t\t\t - n13 * n24 * n32\n\t\t\t\t\t - n14 * n22 * n33\n\t\t\t\t\t + n12 * n24 * n33\n\t\t\t\t\t + n13 * n22 * n34\n\t\t\t\t\t - n12 * n23 * n34\n\t\t\t\t) +\n\t\t\t\tn42 * (\n\t\t\t\t\t+ n11 * n23 * n34\n\t\t\t\t\t - n11 * n24 * n33\n\t\t\t\t\t + n14 * n21 * n33\n\t\t\t\t\t - n13 * n21 * n34\n\t\t\t\t\t + n13 * n24 * n31\n\t\t\t\t\t - n14 * n23 * n31\n\t\t\t\t) +\n\t\t\t\tn43 * (\n\t\t\t\t\t+ n11 * n24 * n32\n\t\t\t\t\t - n11 * n22 * n34\n\t\t\t\t\t - n14 * n21 * n32\n\t\t\t\t\t + n12 * n21 * n34\n\t\t\t\t\t + n14 * n22 * n31\n\t\t\t\t\t - n12 * n24 * n31\n\t\t\t\t) +\n\t\t\t\tn44 * (\n\t\t\t\t\t- n13 * n22 * n31\n\t\t\t\t\t - n11 * n23 * n32\n\t\t\t\t\t + n11 * n22 * n33\n\t\t\t\t\t + n13 * n21 * n32\n\t\t\t\t\t - n12 * n21 * n33\n\t\t\t\t\t + n12 * n23 * n31\n\t\t\t\t)\n\n\t\t\t);\n\n\t\t},\n\n\t\ttranspose: function () {\n\n\t\t\tvar te = this.elements;\n\t\t\tvar tmp;\n\n\t\t\ttmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;\n\t\t\ttmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;\n\t\t\ttmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;\n\n\t\t\ttmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;\n\t\t\ttmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;\n\t\t\ttmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tflattenToArrayOffset: function ( array, offset ) {\n\n\t\t\tconsole.warn( \"THREE.Matrix3: .flattenToArrayOffset is deprecated \" +\n\t\t\t\t\t\"- just use .toArray instead.\" );\n\n\t\t\treturn this.toArray( array, offset );\n\n\t\t},\n\n\t\tgetPosition: function () {\n\n\t\t\tvar v1;\n\n\t\t\treturn function getPosition() {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\t\t\t\tconsole.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );\n\n\t\t\t\treturn v1.setFromMatrixColumn( this, 3 );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tsetPosition: function ( v ) {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tte[ 12 ] = v.x;\n\t\t\tte[ 13 ] = v.y;\n\t\t\tte[ 14 ] = v.z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetInverse: function ( m, throwOnDegenerate ) {\n\n\t\t\t// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n\t\t\tvar te = this.elements,\n\t\t\t\tme = m.elements,\n\n\t\t\t\tn11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],\n\t\t\t\tn12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],\n\t\t\t\tn13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],\n\t\t\t\tn14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],\n\n\t\t\t\tt11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,\n\t\t\t\tt12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,\n\t\t\t\tt13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,\n\t\t\t\tt14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;\n\n\t\t\tvar det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;\n\n\t\t\tif ( det === 0 ) {\n\n\t\t\t\tvar msg = \"THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0\";\n\n\t\t\t\tif ( throwOnDegenerate === true ) {\n\n\t\t\t\t\tthrow new Error( msg );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.warn( msg );\n\n\t\t\t\t}\n\n\t\t\t\treturn this.identity();\n\n\t\t\t}\n\n\t\t\tvar detInv = 1 / det;\n\n\t\t\tte[ 0 ] = t11 * detInv;\n\t\t\tte[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;\n\t\t\tte[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;\n\t\t\tte[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;\n\n\t\t\tte[ 4 ] = t12 * detInv;\n\t\t\tte[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;\n\t\t\tte[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;\n\t\t\tte[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;\n\n\t\t\tte[ 8 ] = t13 * detInv;\n\t\t\tte[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;\n\t\t\tte[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;\n\t\t\tte[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;\n\n\t\t\tte[ 12 ] = t14 * detInv;\n\t\t\tte[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;\n\t\t\tte[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;\n\t\t\tte[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tscale: function ( v ) {\n\n\t\t\tvar te = this.elements;\n\t\t\tvar x = v.x, y = v.y, z = v.z;\n\n\t\t\tte[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;\n\t\t\tte[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;\n\t\t\tte[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;\n\t\t\tte[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetMaxScaleOnAxis: function () {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tvar scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];\n\t\t\tvar scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];\n\t\t\tvar scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];\n\n\t\t\treturn Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );\n\n\t\t},\n\n\t\tmakeTranslation: function ( x, y, z ) {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0, x,\n\t\t\t\t0, 1, 0, y,\n\t\t\t\t0, 0, 1, z,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeRotationX: function ( theta ) {\n\n\t\t\tvar c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0, 0,\n\t\t\t\t0, c, - s, 0,\n\t\t\t\t0, s, c, 0,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeRotationY: function ( theta ) {\n\n\t\t\tvar c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\t\tthis.set(\n\n\t\t\t\t c, 0, s, 0,\n\t\t\t\t 0, 1, 0, 0,\n\t\t\t\t- s, 0, c, 0,\n\t\t\t\t 0, 0, 0, 1\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeRotationZ: function ( theta ) {\n\n\t\t\tvar c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\t\tthis.set(\n\n\t\t\t\tc, - s, 0, 0,\n\t\t\t\ts, c, 0, 0,\n\t\t\t\t0, 0, 1, 0,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeRotationAxis: function ( axis, angle ) {\n\n\t\t\t// Based on http://www.gamedev.net/reference/articles/article1199.asp\n\n\t\t\tvar c = Math.cos( angle );\n\t\t\tvar s = Math.sin( angle );\n\t\t\tvar t = 1 - c;\n\t\t\tvar x = axis.x, y = axis.y, z = axis.z;\n\t\t\tvar tx = t * x, ty = t * y;\n\n\t\t\tthis.set(\n\n\t\t\t\ttx * x + c, tx * y - s * z, tx * z + s * y, 0,\n\t\t\t\ttx * y + s * z, ty * y + c, ty * z - s * x, 0,\n\t\t\t\ttx * z - s * y, ty * z + s * x, t * z * z + c, 0,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t\t return this;\n\n\t\t},\n\n\t\tmakeScale: function ( x, y, z ) {\n\n\t\t\tthis.set(\n\n\t\t\t\tx, 0, 0, 0,\n\t\t\t\t0, y, 0, 0,\n\t\t\t\t0, 0, z, 0,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcompose: function ( position, quaternion, scale ) {\n\n\t\t\tthis.makeRotationFromQuaternion( quaternion );\n\t\t\tthis.scale( scale );\n\t\t\tthis.setPosition( position );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdecompose: function () {\n\n\t\t\tvar vector, matrix;\n\n\t\t\treturn function decompose( position, quaternion, scale ) {\n\n\t\t\t\tif ( vector === undefined ) {\n\n\t\t\t\t\tvector = new Vector3();\n\t\t\t\t\tmatrix = new Matrix4();\n\n\t\t\t\t}\n\n\t\t\t\tvar te = this.elements;\n\n\t\t\t\tvar sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();\n\t\t\t\tvar sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();\n\t\t\t\tvar sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();\n\n\t\t\t\t// if determine is negative, we need to invert one scale\n\t\t\t\tvar det = this.determinant();\n\t\t\t\tif ( det < 0 ) {\n\n\t\t\t\t\tsx = - sx;\n\n\t\t\t\t}\n\n\t\t\t\tposition.x = te[ 12 ];\n\t\t\t\tposition.y = te[ 13 ];\n\t\t\t\tposition.z = te[ 14 ];\n\n\t\t\t\t// scale the rotation part\n\n\t\t\t\tmatrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy()\n\n\t\t\t\tvar invSX = 1 / sx;\n\t\t\t\tvar invSY = 1 / sy;\n\t\t\t\tvar invSZ = 1 / sz;\n\n\t\t\t\tmatrix.elements[ 0 ] *= invSX;\n\t\t\t\tmatrix.elements[ 1 ] *= invSX;\n\t\t\t\tmatrix.elements[ 2 ] *= invSX;\n\n\t\t\t\tmatrix.elements[ 4 ] *= invSY;\n\t\t\t\tmatrix.elements[ 5 ] *= invSY;\n\t\t\t\tmatrix.elements[ 6 ] *= invSY;\n\n\t\t\t\tmatrix.elements[ 8 ] *= invSZ;\n\t\t\t\tmatrix.elements[ 9 ] *= invSZ;\n\t\t\t\tmatrix.elements[ 10 ] *= invSZ;\n\n\t\t\t\tquaternion.setFromRotationMatrix( matrix );\n\n\t\t\t\tscale.x = sx;\n\t\t\t\tscale.y = sy;\n\t\t\t\tscale.z = sz;\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tmakeFrustum: function ( left, right, bottom, top, near, far ) {\n\n\t\t\tvar te = this.elements;\n\t\t\tvar x = 2 * near / ( right - left );\n\t\t\tvar y = 2 * near / ( top - bottom );\n\n\t\t\tvar a = ( right + left ) / ( right - left );\n\t\t\tvar b = ( top + bottom ) / ( top - bottom );\n\t\t\tvar c = - ( far + near ) / ( far - near );\n\t\t\tvar d = - 2 * far * near / ( far - near );\n\n\t\t\tte[ 0 ] = x;\tte[ 4 ] = 0;\tte[ 8 ] = a;\tte[ 12 ] = 0;\n\t\t\tte[ 1 ] = 0;\tte[ 5 ] = y;\tte[ 9 ] = b;\tte[ 13 ] = 0;\n\t\t\tte[ 2 ] = 0;\tte[ 6 ] = 0;\tte[ 10 ] = c;\tte[ 14 ] = d;\n\t\t\tte[ 3 ] = 0;\tte[ 7 ] = 0;\tte[ 11 ] = - 1;\tte[ 15 ] = 0;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakePerspective: function ( fov, aspect, near, far ) {\n\n\t\t\tvar ymax = near * Math.tan( _Math.DEG2RAD * fov * 0.5 );\n\t\t\tvar ymin = - ymax;\n\t\t\tvar xmin = ymin * aspect;\n\t\t\tvar xmax = ymax * aspect;\n\n\t\t\treturn this.makeFrustum( xmin, xmax, ymin, ymax, near, far );\n\n\t\t},\n\n\t\tmakeOrthographic: function ( left, right, top, bottom, near, far ) {\n\n\t\t\tvar te = this.elements;\n\t\t\tvar w = 1.0 / ( right - left );\n\t\t\tvar h = 1.0 / ( top - bottom );\n\t\t\tvar p = 1.0 / ( far - near );\n\n\t\t\tvar x = ( right + left ) * w;\n\t\t\tvar y = ( top + bottom ) * h;\n\t\t\tvar z = ( far + near ) * p;\n\n\t\t\tte[ 0 ] = 2 * w;\tte[ 4 ] = 0;\tte[ 8 ] = 0;\tte[ 12 ] = - x;\n\t\t\tte[ 1 ] = 0;\tte[ 5 ] = 2 * h;\tte[ 9 ] = 0;\tte[ 13 ] = - y;\n\t\t\tte[ 2 ] = 0;\tte[ 6 ] = 0;\tte[ 10 ] = - 2 * p;\tte[ 14 ] = - z;\n\t\t\tte[ 3 ] = 0;\tte[ 7 ] = 0;\tte[ 11 ] = 0;\tte[ 15 ] = 1;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( matrix ) {\n\n\t\t\tvar te = this.elements;\n\t\t\tvar me = matrix.elements;\n\n\t\t\tfor ( var i = 0; i < 16; i ++ ) {\n\n\t\t\t\tif ( te[ i ] !== me[ i ] ) return false;\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t},\n\n\t\tfromArray: function ( array, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tfor( var i = 0; i < 16; i ++ ) {\n\n\t\t\t\tthis.elements[ i ] = array[ i + offset ];\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tvar te = this.elements;\n\n\t\t\tarray[ offset ] = te[ 0 ];\n\t\t\tarray[ offset + 1 ] = te[ 1 ];\n\t\t\tarray[ offset + 2 ] = te[ 2 ];\n\t\t\tarray[ offset + 3 ] = te[ 3 ];\n\n\t\t\tarray[ offset + 4 ] = te[ 4 ];\n\t\t\tarray[ offset + 5 ] = te[ 5 ];\n\t\t\tarray[ offset + 6 ] = te[ 6 ];\n\t\t\tarray[ offset + 7 ] = te[ 7 ];\n\n\t\t\tarray[ offset + 8 ] = te[ 8 ];\n\t\t\tarray[ offset + 9 ] = te[ 9 ];\n\t\t\tarray[ offset + 10 ] = te[ 10 ];\n\t\t\tarray[ offset + 11 ] = te[ 11 ];\n\n\t\t\tarray[ offset + 12 ] = te[ 12 ];\n\t\t\tarray[ offset + 13 ] = te[ 13 ];\n\t\t\tarray[ offset + 14 ] = te[ 14 ];\n\t\t\tarray[ offset + 15 ] = te[ 15 ];\n\n\t\t\treturn array;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {\n\n\t\timages = images !== undefined ? images : [];\n\t\tmapping = mapping !== undefined ? mapping : CubeReflectionMapping;\n\n\t\tTexture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n\t\tthis.flipY = false;\n\n\t}\n\n\tCubeTexture.prototype = Object.create( Texture.prototype );\n\tCubeTexture.prototype.constructor = CubeTexture;\n\n\tCubeTexture.prototype.isCubeTexture = true;\n\n\tObject.defineProperty( CubeTexture.prototype, 'images', {\n\n\t\tget: function () {\n\n\t\t\treturn this.image;\n\n\t\t},\n\n\t\tset: function ( value ) {\n\n\t\t\tthis.image = value;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author tschw\n\t *\n\t * Uniforms of a program.\n\t * Those form a tree structure with a special top-level container for the root,\n\t * which you get by calling 'new WebGLUniforms( gl, program, renderer )'.\n\t *\n\t *\n\t * Properties of inner nodes including the top-level container:\n\t *\n\t * .seq - array of nested uniforms\n\t * .map - nested uniforms by name\n\t *\n\t *\n\t * Methods of all nodes except the top-level container:\n\t *\n\t * .setValue( gl, value, [renderer] )\n\t *\n\t * \t\tuploads a uniform value(s)\n\t * \tthe 'renderer' parameter is needed for sampler uniforms\n\t *\n\t *\n\t * Static methods of the top-level container (renderer factorizations):\n\t *\n\t * .upload( gl, seq, values, renderer )\n\t *\n\t * \t\tsets uniforms in 'seq' to 'values[id].value'\n\t *\n\t * .seqWithValue( seq, values ) : filteredSeq\n\t *\n\t * \t\tfilters 'seq' entries with corresponding entry in values\n\t *\n\t *\n\t * Methods of the top-level container (renderer factorizations):\n\t *\n\t * .setValue( gl, name, value )\n\t *\n\t * \t\tsets uniform with name 'name' to 'value'\n\t *\n\t * .set( gl, obj, prop )\n\t *\n\t * \t\tsets uniform from object and property with same name than uniform\n\t *\n\t * .setOptional( gl, obj, prop )\n\t *\n\t * \t\tlike .set for an optional property of the object\n\t *\n\t */\n\n\tvar emptyTexture = new Texture();\n\tvar emptyCubeTexture = new CubeTexture();\n\n\t// --- Base for inner nodes (including the root) ---\n\n\tfunction UniformContainer() {\n\n\t\tthis.seq = [];\n\t\tthis.map = {};\n\n\t}\n\n\t// --- Utilities ---\n\n\t// Array Caches (provide typed arrays for temporary by size)\n\n\tvar arrayCacheF32 = [];\n\tvar arrayCacheI32 = [];\n\n\t// Flattening for arrays of vectors and matrices\n\n\tfunction flatten( array, nBlocks, blockSize ) {\n\n\t\tvar firstElem = array[ 0 ];\n\n\t\tif ( firstElem <= 0 || firstElem > 0 ) return array;\n\t\t// unoptimized: ! isNaN( firstElem )\n\t\t// see http://jacksondunstan.com/articles/983\n\n\t\tvar n = nBlocks * blockSize,\n\t\t\tr = arrayCacheF32[ n ];\n\n\t\tif ( r === undefined ) {\n\n\t\t\tr = new Float32Array( n );\n\t\t\tarrayCacheF32[ n ] = r;\n\n\t\t}\n\n\t\tif ( nBlocks !== 0 ) {\n\n\t\t\tfirstElem.toArray( r, 0 );\n\n\t\t\tfor ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {\n\n\t\t\t\toffset += blockSize;\n\t\t\t\tarray[ i ].toArray( r, offset );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn r;\n\n\t}\n\n\t// Texture unit allocation\n\n\tfunction allocTexUnits( renderer, n ) {\n\n\t\tvar r = arrayCacheI32[ n ];\n\n\t\tif ( r === undefined ) {\n\n\t\t\tr = new Int32Array( n );\n\t\t\tarrayCacheI32[ n ] = r;\n\n\t\t}\n\n\t\tfor ( var i = 0; i !== n; ++ i )\n\t\t\tr[ i ] = renderer.allocTextureUnit();\n\n\t\treturn r;\n\n\t}\n\n\t// --- Setters ---\n\n\t// Note: Defining these methods externally, because they come in a bunch\n\t// and this way their names minify.\n\n\t// Single scalar\n\n\tfunction setValue1f( gl, v ) { gl.uniform1f( this.addr, v ); }\n\tfunction setValue1i( gl, v ) { gl.uniform1i( this.addr, v ); }\n\n\t// Single float vector (from flat array or THREE.VectorN)\n\n\tfunction setValue2fv( gl, v ) {\n\n\t\tif ( v.x === undefined ) gl.uniform2fv( this.addr, v );\n\t\telse gl.uniform2f( this.addr, v.x, v.y );\n\n\t}\n\n\tfunction setValue3fv( gl, v ) {\n\n\t\tif ( v.x !== undefined )\n\t\t\tgl.uniform3f( this.addr, v.x, v.y, v.z );\n\t\telse if ( v.r !== undefined )\n\t\t\tgl.uniform3f( this.addr, v.r, v.g, v.b );\n\t\telse\n\t\t\tgl.uniform3fv( this.addr, v );\n\n\t}\n\n\tfunction setValue4fv( gl, v ) {\n\n\t\tif ( v.x === undefined ) gl.uniform4fv( this.addr, v );\n\t\telse gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );\n\n\t}\n\n\t// Single matrix (from flat array or MatrixN)\n\n\tfunction setValue2fm( gl, v ) {\n\n\t\tgl.uniformMatrix2fv( this.addr, false, v.elements || v );\n\n\t}\n\n\tfunction setValue3fm( gl, v ) {\n\n\t\tgl.uniformMatrix3fv( this.addr, false, v.elements || v );\n\n\t}\n\n\tfunction setValue4fm( gl, v ) {\n\n\t\tgl.uniformMatrix4fv( this.addr, false, v.elements || v );\n\n\t}\n\n\t// Single texture (2D / Cube)\n\n\tfunction setValueT1( gl, v, renderer ) {\n\n\t\tvar unit = renderer.allocTextureUnit();\n\t\tgl.uniform1i( this.addr, unit );\n\t\trenderer.setTexture2D( v || emptyTexture, unit );\n\n\t}\n\n\tfunction setValueT6( gl, v, renderer ) {\n\n\t\tvar unit = renderer.allocTextureUnit();\n\t\tgl.uniform1i( this.addr, unit );\n\t\trenderer.setTextureCube( v || emptyCubeTexture, unit );\n\n\t}\n\n\t// Integer / Boolean vectors or arrays thereof (always flat arrays)\n\n\tfunction setValue2iv( gl, v ) { gl.uniform2iv( this.addr, v ); }\n\tfunction setValue3iv( gl, v ) { gl.uniform3iv( this.addr, v ); }\n\tfunction setValue4iv( gl, v ) { gl.uniform4iv( this.addr, v ); }\n\n\t// Helper to pick the right setter for the singular case\n\n\tfunction getSingularSetter( type ) {\n\n\t\tswitch ( type ) {\n\n\t\t\tcase 0x1406: return setValue1f; // FLOAT\n\t\t\tcase 0x8b50: return setValue2fv; // _VEC2\n\t\t\tcase 0x8b51: return setValue3fv; // _VEC3\n\t\t\tcase 0x8b52: return setValue4fv; // _VEC4\n\n\t\t\tcase 0x8b5a: return setValue2fm; // _MAT2\n\t\t\tcase 0x8b5b: return setValue3fm; // _MAT3\n\t\t\tcase 0x8b5c: return setValue4fm; // _MAT4\n\n\t\t\tcase 0x8b5e: return setValueT1; // SAMPLER_2D\n\t\t\tcase 0x8b60: return setValueT6; // SAMPLER_CUBE\n\n\t\t\tcase 0x1404: case 0x8b56: return setValue1i; // INT, BOOL\n\t\t\tcase 0x8b53: case 0x8b57: return setValue2iv; // _VEC2\n\t\t\tcase 0x8b54: case 0x8b58: return setValue3iv; // _VEC3\n\t\t\tcase 0x8b55: case 0x8b59: return setValue4iv; // _VEC4\n\n\t\t}\n\n\t}\n\n\t// Array of scalars\n\n\tfunction setValue1fv( gl, v ) { gl.uniform1fv( this.addr, v ); }\n\tfunction setValue1iv( gl, v ) { gl.uniform1iv( this.addr, v ); }\n\n\t// Array of vectors (flat or from THREE classes)\n\n\tfunction setValueV2a( gl, v ) {\n\n\t\tgl.uniform2fv( this.addr, flatten( v, this.size, 2 ) );\n\n\t}\n\n\tfunction setValueV3a( gl, v ) {\n\n\t\tgl.uniform3fv( this.addr, flatten( v, this.size, 3 ) );\n\n\t}\n\n\tfunction setValueV4a( gl, v ) {\n\n\t\tgl.uniform4fv( this.addr, flatten( v, this.size, 4 ) );\n\n\t}\n\n\t// Array of matrices (flat or from THREE clases)\n\n\tfunction setValueM2a( gl, v ) {\n\n\t\tgl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) );\n\n\t}\n\n\tfunction setValueM3a( gl, v ) {\n\n\t\tgl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) );\n\n\t}\n\n\tfunction setValueM4a( gl, v ) {\n\n\t\tgl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) );\n\n\t}\n\n\t// Array of textures (2D / Cube)\n\n\tfunction setValueT1a( gl, v, renderer ) {\n\n\t\tvar n = v.length,\n\t\t\tunits = allocTexUnits( renderer, n );\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tfor ( var i = 0; i !== n; ++ i ) {\n\n\t\t\trenderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );\n\n\t\t}\n\n\t}\n\n\tfunction setValueT6a( gl, v, renderer ) {\n\n\t\tvar n = v.length,\n\t\t\tunits = allocTexUnits( renderer, n );\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tfor ( var i = 0; i !== n; ++ i ) {\n\n\t\t\trenderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );\n\n\t\t}\n\n\t}\n\n\t// Helper to pick the right setter for a pure (bottom-level) array\n\n\tfunction getPureArraySetter( type ) {\n\n\t\tswitch ( type ) {\n\n\t\t\tcase 0x1406: return setValue1fv; // FLOAT\n\t\t\tcase 0x8b50: return setValueV2a; // _VEC2\n\t\t\tcase 0x8b51: return setValueV3a; // _VEC3\n\t\t\tcase 0x8b52: return setValueV4a; // _VEC4\n\n\t\t\tcase 0x8b5a: return setValueM2a; // _MAT2\n\t\t\tcase 0x8b5b: return setValueM3a; // _MAT3\n\t\t\tcase 0x8b5c: return setValueM4a; // _MAT4\n\n\t\t\tcase 0x8b5e: return setValueT1a; // SAMPLER_2D\n\t\t\tcase 0x8b60: return setValueT6a; // SAMPLER_CUBE\n\n\t\t\tcase 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL\n\t\t\tcase 0x8b53: case 0x8b57: return setValue2iv; // _VEC2\n\t\t\tcase 0x8b54: case 0x8b58: return setValue3iv; // _VEC3\n\t\t\tcase 0x8b55: case 0x8b59: return setValue4iv; // _VEC4\n\n\t\t}\n\n\t}\n\n\t// --- Uniform Classes ---\n\n\tfunction SingleUniform( id, activeInfo, addr ) {\n\n\t\tthis.id = id;\n\t\tthis.addr = addr;\n\t\tthis.setValue = getSingularSetter( activeInfo.type );\n\n\t\t// this.path = activeInfo.name; // DEBUG\n\n\t}\n\n\tfunction PureArrayUniform( id, activeInfo, addr ) {\n\n\t\tthis.id = id;\n\t\tthis.addr = addr;\n\t\tthis.size = activeInfo.size;\n\t\tthis.setValue = getPureArraySetter( activeInfo.type );\n\n\t\t// this.path = activeInfo.name; // DEBUG\n\n\t}\n\n\tfunction StructuredUniform( id ) {\n\n\t\tthis.id = id;\n\n\t\tUniformContainer.call( this ); // mix-in\n\n\t}\n\n\tStructuredUniform.prototype.setValue = function( gl, value ) {\n\n\t\t// Note: Don't need an extra 'renderer' parameter, since samplers\n\t\t// are not allowed in structured uniforms.\n\n\t\tvar seq = this.seq;\n\n\t\tfor ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tvar u = seq[ i ];\n\t\t\tu.setValue( gl, value[ u.id ] );\n\n\t\t}\n\n\t};\n\n\t// --- Top-level ---\n\n\t// Parser - builds up the property tree from the path strings\n\n\tvar RePathPart = /([\\w\\d_]+)(\\])?(\\[|\\.)?/g;\n\n\t// extracts\n\t// \t- the identifier (member name or array index)\n\t// - followed by an optional right bracket (found when array index)\n\t// - followed by an optional left bracket or dot (type of subscript)\n\t//\n\t// Note: These portions can be read in a non-overlapping fashion and\n\t// allow straightforward parsing of the hierarchy that WebGL encodes\n\t// in the uniform names.\n\n\tfunction addUniform( container, uniformObject ) {\n\n\t\tcontainer.seq.push( uniformObject );\n\t\tcontainer.map[ uniformObject.id ] = uniformObject;\n\n\t}\n\n\tfunction parseUniform( activeInfo, addr, container ) {\n\n\t\tvar path = activeInfo.name,\n\t\t\tpathLength = path.length;\n\n\t\t// reset RegExp object, because of the early exit of a previous run\n\t\tRePathPart.lastIndex = 0;\n\n\t\tfor (; ;) {\n\n\t\t\tvar match = RePathPart.exec( path ),\n\t\t\t\tmatchEnd = RePathPart.lastIndex,\n\n\t\t\t\tid = match[ 1 ],\n\t\t\t\tidIsIndex = match[ 2 ] === ']',\n\t\t\t\tsubscript = match[ 3 ];\n\n\t\t\tif ( idIsIndex ) id = id | 0; // convert to integer\n\n\t\t\tif ( subscript === undefined ||\n\t\t\t\t\tsubscript === '[' && matchEnd + 2 === pathLength ) {\n\t\t\t\t// bare name or \"pure\" bottom-level array \"[0]\" suffix\n\n\t\t\t\taddUniform( container, subscript === undefined ?\n\t\t\t\t\t\tnew SingleUniform( id, activeInfo, addr ) :\n\t\t\t\t\t\tnew PureArrayUniform( id, activeInfo, addr ) );\n\n\t\t\t\tbreak;\n\n\t\t\t} else {\n\t\t\t\t// step into inner node / create it in case it doesn't exist\n\n\t\t\t\tvar map = container.map,\n\t\t\t\t\tnext = map[ id ];\n\n\t\t\t\tif ( next === undefined ) {\n\n\t\t\t\t\tnext = new StructuredUniform( id );\n\t\t\t\t\taddUniform( container, next );\n\n\t\t\t\t}\n\n\t\t\t\tcontainer = next;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t// Root Container\n\n\tfunction WebGLUniforms( gl, program, renderer ) {\n\n\t\tUniformContainer.call( this );\n\n\t\tthis.renderer = renderer;\n\n\t\tvar n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );\n\n\t\tfor ( var i = 0; i !== n; ++ i ) {\n\n\t\t\tvar info = gl.getActiveUniform( program, i ),\n\t\t\t\tpath = info.name,\n\t\t\t\taddr = gl.getUniformLocation( program, path );\n\n\t\t\tparseUniform( info, addr, this );\n\n\t\t}\n\n\t}\n\n\tWebGLUniforms.prototype.setValue = function( gl, name, value ) {\n\n\t\tvar u = this.map[ name ];\n\n\t\tif ( u !== undefined ) u.setValue( gl, value, this.renderer );\n\n\t};\n\n\tWebGLUniforms.prototype.set = function( gl, object, name ) {\n\n\t\tvar u = this.map[ name ];\n\n\t\tif ( u !== undefined ) u.setValue( gl, object[ name ], this.renderer );\n\n\t};\n\n\tWebGLUniforms.prototype.setOptional = function( gl, object, name ) {\n\n\t\tvar v = object[ name ];\n\n\t\tif ( v !== undefined ) this.setValue( gl, name, v );\n\n\t};\n\n\n\t// Static interface\n\n\tWebGLUniforms.upload = function( gl, seq, values, renderer ) {\n\n\t\tfor ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tvar u = seq[ i ],\n\t\t\t\tv = values[ u.id ];\n\n\t\t\tif ( v.needsUpdate !== false ) {\n\t\t\t\t// note: always updating when .needsUpdate is undefined\n\n\t\t\t\tu.setValue( gl, v.value, renderer );\n\n\t\t\t}\n\n\t\t}\n\n\t};\n\n\tWebGLUniforms.seqWithValue = function( seq, values ) {\n\n\t\tvar r = [];\n\n\t\tfor ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tvar u = seq[ i ];\n\t\t\tif ( u.id in values ) r.push( u );\n\n\t\t}\n\n\t\treturn r;\n\n\t};\n\n\t/**\n\t * Uniform Utilities\n\t */\n\n\tvar UniformsUtils = {\n\n\t\tmerge: function ( uniforms ) {\n\n\t\t\tvar merged = {};\n\n\t\t\tfor ( var u = 0; u < uniforms.length; u ++ ) {\n\n\t\t\t\tvar tmp = this.clone( uniforms[ u ] );\n\n\t\t\t\tfor ( var p in tmp ) {\n\n\t\t\t\t\tmerged[ p ] = tmp[ p ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn merged;\n\n\t\t},\n\n\t\tclone: function ( uniforms_src ) {\n\n\t\t\tvar uniforms_dst = {};\n\n\t\t\tfor ( var u in uniforms_src ) {\n\n\t\t\t\tuniforms_dst[ u ] = {};\n\n\t\t\t\tfor ( var p in uniforms_src[ u ] ) {\n\n\t\t\t\t\tvar parameter_src = uniforms_src[ u ][ p ];\n\n\t\t\t\t\tif ( parameter_src && ( parameter_src.isColor ||\n\t\t\t\t\t\tparameter_src.isMatrix3 || parameter_src.isMatrix4 ||\n\t\t\t\t\t\tparameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 ||\n\t\t\t\t\t\tparameter_src.isTexture ) ) {\n\n\t\t\t\t\t\tuniforms_dst[ u ][ p ] = parameter_src.clone();\n\n\t\t\t\t\t} else if ( Array.isArray( parameter_src ) ) {\n\n\t\t\t\t\t\tuniforms_dst[ u ][ p ] = parameter_src.slice();\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tuniforms_dst[ u ][ p ] = parameter_src;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn uniforms_dst;\n\n\t\t}\n\n\t};\n\n\tvar alphamap_fragment = \"#ifdef USE_ALPHAMAP\\n\\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\\n#endif\\n\";\n\n\tvar alphamap_pars_fragment = \"#ifdef USE_ALPHAMAP\\n\\tuniform sampler2D alphaMap;\\n#endif\\n\";\n\n\tvar alphatest_fragment = \"#ifdef ALPHATEST\\n\\tif ( diffuseColor.a < ALPHATEST ) discard;\\n#endif\\n\";\n\n\tvar aomap_fragment = \"#ifdef USE_AOMAP\\n\\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\\n\\treflectedLight.indirectDiffuse *= ambientOcclusion;\\n\\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\\n\\t\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\t\\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\\n\\t#endif\\n#endif\\n\";\n\n\tvar aomap_pars_fragment = \"#ifdef USE_AOMAP\\n\\tuniform sampler2D aoMap;\\n\\tuniform float aoMapIntensity;\\n#endif\";\n\n\tvar begin_vertex = \"\\nvec3 transformed = vec3( position );\\n\";\n\n\tvar beginnormal_vertex = \"\\nvec3 objectNormal = vec3( normal );\\n\";\n\n\tvar bsdfs = \"bool testLightInRange( const in float lightDistance, const in float cutoffDistance ) {\\n\\treturn any( bvec2( cutoffDistance == 0.0, lightDistance < cutoffDistance ) );\\n}\\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\\n\\t\\tif( decayExponent > 0.0 ) {\\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\\n\\t\\t\\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\\n\\t\\t\\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\\n\\t\\t\\treturn distanceFalloff * maxDistanceCutoffFactor;\\n#else\\n\\t\\t\\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\\n#endif\\n\\t\\t}\\n\\t\\treturn 1.0;\\n}\\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\\n\\treturn RECIPROCAL_PI * diffuseColor;\\n}\\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\\n\\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\\n\\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\\n}\\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\\n\\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\\n\\treturn 1.0 / ( gl * gv );\\n}\\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\\n\\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\\n\\treturn 0.5 / max( gv + gl, EPSILON );\\n}\\nfloat D_GGX( const in float alpha, const in float dotNH ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\\n\\treturn RECIPROCAL_PI * a2 / pow2( denom );\\n}\\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\\n\\tfloat alpha = pow2( roughness );\\n\\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\\n\\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\\n\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\\n\\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\\n\\tvec3 F = F_Schlick( specularColor, dotLH );\\n\\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\\n\\tfloat D = D_GGX( alpha, dotNH );\\n\\treturn F * ( G * D );\\n}\\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\\n\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\\n\\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\\n\\tvec4 r = roughness * c0 + c1;\\n\\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\\n\\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\\n\\treturn specularColor * AB.x + AB.y;\\n}\\nfloat G_BlinnPhong_Implicit( ) {\\n\\treturn 0.25;\\n}\\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\\n\\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\\n}\\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\\n\\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\\n\\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\\n\\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\\n\\tvec3 F = F_Schlick( specularColor, dotLH );\\n\\tfloat G = G_BlinnPhong_Implicit( );\\n\\tfloat D = D_BlinnPhong( shininess, dotNH );\\n\\treturn F * ( G * D );\\n}\\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\\n\\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\\n}\\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\\n\\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\\n}\\n\";\n\n\tvar bumpmap_pars_fragment = \"#ifdef USE_BUMPMAP\\n\\tuniform sampler2D bumpMap;\\n\\tuniform float bumpScale;\\n\\tvec2 dHdxy_fwd() {\\n\\t\\tvec2 dSTdx = dFdx( vUv );\\n\\t\\tvec2 dSTdy = dFdy( vUv );\\n\\t\\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\\n\\t\\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\\n\\t\\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\\n\\t\\treturn vec2( dBx, dBy );\\n\\t}\\n\\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\\n\\t\\tvec3 vSigmaX = dFdx( surf_pos );\\n\\t\\tvec3 vSigmaY = dFdy( surf_pos );\\n\\t\\tvec3 vN = surf_norm;\\n\\t\\tvec3 R1 = cross( vSigmaY, vN );\\n\\t\\tvec3 R2 = cross( vN, vSigmaX );\\n\\t\\tfloat fDet = dot( vSigmaX, R1 );\\n\\t\\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\\n\\t\\treturn normalize( abs( fDet ) * surf_norm - vGrad );\\n\\t}\\n#endif\\n\";\n\n\tvar clipping_planes_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; ++ i ) {\\n\\t\\tvec4 plane = clippingPlanes[ i ];\\n\\t\\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\\n\\t}\\n\\t\\t\\n\\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\\n\\t\\tbool clipped = true;\\n\\t\\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; ++ i ) {\\n\\t\\t\\tvec4 plane = clippingPlanes[ i ];\\n\\t\\t\\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\\n\\t\\t}\\n\\t\\tif ( clipped ) discard;\\n\\t\\n\\t#endif\\n#endif\\n\";\n\n\tvar clipping_planes_pars_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\t\\tvarying vec3 vViewPosition;\\n\\t#endif\\n\\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\\n#endif\\n\";\n\n\tvar clipping_planes_pars_vertex = \"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n\";\n\n\tvar clipping_planes_vertex = \"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\tvViewPosition = - mvPosition.xyz;\\n#endif\\n\";\n\n\tvar color_fragment = \"#ifdef USE_COLOR\\n\\tdiffuseColor.rgb *= vColor;\\n#endif\";\n\n\tvar color_pars_fragment = \"#ifdef USE_COLOR\\n\\tvarying vec3 vColor;\\n#endif\\n\";\n\n\tvar color_pars_vertex = \"#ifdef USE_COLOR\\n\\tvarying vec3 vColor;\\n#endif\";\n\n\tvar color_vertex = \"#ifdef USE_COLOR\\n\\tvColor.xyz = color.xyz;\\n#endif\";\n\n\tvar common = \"#define PI 3.14159265359\\n#define PI2 6.28318530718\\n#define RECIPROCAL_PI 0.31830988618\\n#define RECIPROCAL_PI2 0.15915494\\n#define LOG2 1.442695\\n#define EPSILON 1e-6\\n#define saturate(a) clamp( a, 0.0, 1.0 )\\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\\nfloat pow2( const in float x ) { return x*x; }\\nfloat pow3( const in float x ) { return x*x*x; }\\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\\nhighp float rand( const in vec2 uv ) {\\n\\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\\n\\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\\n\\treturn fract(sin(sn) * c);\\n}\\nstruct IncidentLight {\\n\\tvec3 color;\\n\\tvec3 direction;\\n\\tbool visible;\\n};\\nstruct ReflectedLight {\\n\\tvec3 directDiffuse;\\n\\tvec3 directSpecular;\\n\\tvec3 indirectDiffuse;\\n\\tvec3 indirectSpecular;\\n};\\nstruct GeometricContext {\\n\\tvec3 position;\\n\\tvec3 normal;\\n\\tvec3 viewDir;\\n};\\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\\n}\\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\\n}\\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\tfloat distance = dot( planeNormal, point - pointOnPlane );\\n\\treturn - distance * planeNormal + point;\\n}\\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\treturn sign( dot( point - pointOnPlane, planeNormal ) );\\n}\\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\\n}\\n\";\n\n\tvar cube_uv_reflection_fragment = \"#ifdef ENVMAP_TYPE_CUBE_UV\\n#define cubeUV_textureSize (1024.0)\\nint getFaceFromDirection(vec3 direction) {\\n\\tvec3 absDirection = abs(direction);\\n\\tint face = -1;\\n\\tif( absDirection.x > absDirection.z ) {\\n\\t\\tif(absDirection.x > absDirection.y )\\n\\t\\t\\tface = direction.x > 0.0 ? 0 : 3;\\n\\t\\telse\\n\\t\\t\\tface = direction.y > 0.0 ? 1 : 4;\\n\\t}\\n\\telse {\\n\\t\\tif(absDirection.z > absDirection.y )\\n\\t\\t\\tface = direction.z > 0.0 ? 2 : 5;\\n\\t\\telse\\n\\t\\t\\tface = direction.y > 0.0 ? 1 : 4;\\n\\t}\\n\\treturn face;\\n}\\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\\n\\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\\n\\tfloat dxRoughness = dFdx(roughness);\\n\\tfloat dyRoughness = dFdy(roughness);\\n\\tvec3 dx = dFdx( vec * scale * dxRoughness );\\n\\tvec3 dy = dFdy( vec * scale * dyRoughness );\\n\\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\\n\\td = clamp(d, 1.0, cubeUV_rangeClamp);\\n\\tfloat mipLevel = 0.5 * log2(d);\\n\\treturn vec2(floor(mipLevel), fract(mipLevel));\\n}\\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\\n\\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\\n\\tfloat a = 16.0 * cubeUV_rcpTextureSize;\\n\\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\\n\\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\\n\\tfloat powScale = exp2_packed.x * exp2_packed.y;\\n\\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\\n\\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\\n\\tbool bRes = mipLevel == 0.0;\\n\\tscale = bRes && (scale < a) ? a : scale;\\n\\tvec3 r;\\n\\tvec2 offset;\\n\\tint face = getFaceFromDirection(direction);\\n\\tfloat rcpPowScale = 1.0 / powScale;\\n\\tif( face == 0) {\\n\\t\\tr = vec3(direction.x, -direction.z, direction.y);\\n\\t\\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 1) {\\n\\t\\tr = vec3(direction.y, direction.x, direction.z);\\n\\t\\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 2) {\\n\\t\\tr = vec3(direction.z, direction.x, direction.y);\\n\\t\\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 3) {\\n\\t\\tr = vec3(direction.x, direction.z, direction.y);\\n\\t\\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\telse if( face == 4) {\\n\\t\\tr = vec3(direction.y, direction.x, -direction.z);\\n\\t\\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\telse {\\n\\t\\tr = vec3(direction.z, -direction.x, direction.y);\\n\\t\\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\tr = normalize(r);\\n\\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\\n\\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\\n\\tvec2 base = offset + vec2( texelOffset );\\n\\treturn base + s * ( scale - 2.0 * texelOffset );\\n}\\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\\n\\tfloat roughnessVal = roughness* cubeUV_maxLods3;\\n\\tfloat r1 = floor(roughnessVal);\\n\\tfloat r2 = r1 + 1.0;\\n\\tfloat t = fract(roughnessVal);\\n\\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\\n\\tfloat s = mipInfo.y;\\n\\tfloat level0 = mipInfo.x;\\n\\tfloat level1 = level0 + 1.0;\\n\\tlevel1 = level1 > 5.0 ? 5.0 : level1;\\n\\tlevel0 += min( floor( s + 0.5 ), 5.0 );\\n\\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\\n\\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\\n\\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\\n\\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\\n\\tvec4 result = mix(color10, color20, t);\\n\\treturn vec4(result.rgb, 1.0);\\n}\\n#endif\\n\";\n\n\tvar defaultnormal_vertex = \"#ifdef FLIP_SIDED\\n\\tobjectNormal = -objectNormal;\\n#endif\\nvec3 transformedNormal = normalMatrix * objectNormal;\\n\";\n\n\tvar displacementmap_pars_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\tuniform sampler2D displacementMap;\\n\\tuniform float displacementScale;\\n\\tuniform float displacementBias;\\n#endif\\n\";\n\n\tvar displacementmap_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\ttransformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\\n#endif\\n\";\n\n\tvar emissivemap_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\\n\\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\\n\\ttotalEmissiveRadiance *= emissiveColor.rgb;\\n#endif\\n\";\n\n\tvar emissivemap_pars_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tuniform sampler2D emissiveMap;\\n#endif\\n\";\n\n\tvar encodings_fragment = \" gl_FragColor = linearToOutputTexel( gl_FragColor );\\n\";\n\n\tvar encodings_pars_fragment = \"\\nvec4 LinearToLinear( in vec4 value ) {\\n return value;\\n}\\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\\n return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\\n}\\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\\n return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\\n}\\nvec4 sRGBToLinear( in vec4 value ) {\\n return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\\n}\\nvec4 LinearTosRGB( in vec4 value ) {\\n return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\\n}\\nvec4 RGBEToLinear( in vec4 value ) {\\n return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\\n}\\nvec4 LinearToRGBE( in vec4 value ) {\\n float maxComponent = max( max( value.r, value.g ), value.b );\\n float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\\n return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\\n}\\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\\n return vec4( value.xyz * value.w * maxRange, 1.0 );\\n}\\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\\n float maxRGB = max( value.x, max( value.g, value.b ) );\\n float M = clamp( maxRGB / maxRange, 0.0, 1.0 );\\n M = ceil( M * 255.0 ) / 255.0;\\n return vec4( value.rgb / ( M * maxRange ), M );\\n}\\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\\n return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\\n}\\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\\n float maxRGB = max( value.x, max( value.g, value.b ) );\\n float D = max( maxRange / maxRGB, 1.0 );\\n D = min( floor( D ) / 255.0, 1.0 );\\n return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\\n}\\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\\nvec4 LinearToLogLuv( in vec4 value ) {\\n vec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\\n Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\\n vec4 vResult;\\n vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\\n float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\\n vResult.w = fract(Le);\\n vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\\n return vResult;\\n}\\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\\nvec4 LogLuvToLinear( in vec4 value ) {\\n float Le = value.z * 255.0 + value.w;\\n vec3 Xp_Y_XYZp;\\n Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\\n Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\\n Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\\n vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\\n return vec4( max(vRGB, 0.0), 1.0 );\\n}\\n\";\n\n\tvar envmap_fragment = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\\n\\t\\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#else\\n\\t\\tvec3 reflectVec = vReflect;\\n\\t#endif\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tvec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\\n\\t#elif defined( ENVMAP_TYPE_EQUIREC )\\n\\t\\tvec2 sampleUV;\\n\\t\\tsampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\\n\\t\\tsampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\\n\\t\\tvec4 envColor = texture2D( envMap, sampleUV );\\n\\t#elif defined( ENVMAP_TYPE_SPHERE )\\n\\t\\tvec3 reflectView = flipNormal * normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\\n\\t\\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\\n\\t#else\\n\\t\\tvec4 envColor = vec4( 0.0 );\\n\\t#endif\\n\\tenvColor = envMapTexelToLinear( envColor );\\n\\t#ifdef ENVMAP_BLENDING_MULTIPLY\\n\\t\\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_MIX )\\n\\t\\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_ADD )\\n\\t\\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\\n\\t#endif\\n#endif\\n\";\n\n\tvar envmap_pars_fragment = \"#if defined( USE_ENVMAP ) || defined( PHYSICAL )\\n\\tuniform float reflectivity;\\n\\tuniform float envMapIntenstiy;\\n#endif\\n#ifdef USE_ENVMAP\\n\\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t#endif\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tuniform samplerCube envMap;\\n\\t#else\\n\\t\\tuniform sampler2D envMap;\\n\\t#endif\\n\\tuniform float flipEnvMap;\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\\n\\t\\tuniform float refractionRatio;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t#endif\\n#endif\\n\";\n\n\tvar envmap_pars_vertex = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t\\tuniform float refractionRatio;\\n\\t#endif\\n#endif\\n\";\n\n\tvar envmap_vertex = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvWorldPosition = worldPosition.xyz;\\n\\t#else\\n\\t\\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\\n\\t\\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvReflect = reflect( cameraToVertex, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#endif\\n#endif\\n\";\n\n\tvar fog_fragment = \"#ifdef USE_FOG\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\\n\\t#else\\n\\t\\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\\n\\t#endif\\n\\t#ifdef FOG_EXP2\\n\\t\\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\\n\\t#else\\n\\t\\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\\n\\t#endif\\n\\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\\n#endif\\n\";\n\n\tvar fog_pars_fragment = \"#ifdef USE_FOG\\n\\tuniform vec3 fogColor;\\n\\t#ifdef FOG_EXP2\\n\\t\\tuniform float fogDensity;\\n\\t#else\\n\\t\\tuniform float fogNear;\\n\\t\\tuniform float fogFar;\\n\\t#endif\\n#endif\";\n\n\tvar lightmap_fragment = \"#ifdef USE_LIGHTMAP\\n\\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n#endif\\n\";\n\n\tvar lightmap_pars_fragment = \"#ifdef USE_LIGHTMAP\\n\\tuniform sampler2D lightMap;\\n\\tuniform float lightMapIntensity;\\n#endif\";\n\n\tvar lights_lambert_vertex = \"vec3 diffuse = vec3( 1.0 );\\nGeometricContext geometry;\\ngeometry.position = mvPosition.xyz;\\ngeometry.normal = normalize( transformedNormal );\\ngeometry.viewDir = normalize( -mvPosition.xyz );\\nGeometricContext backGeometry;\\nbackGeometry.position = geometry.position;\\nbackGeometry.normal = -geometry.normal;\\nbackGeometry.viewDir = geometry.viewDir;\\nvLightFront = vec3( 0.0 );\\n#ifdef DOUBLE_SIDED\\n\\tvLightBack = vec3( 0.0 );\\n#endif\\nIncidentLight directLight;\\nfloat dotNL;\\nvec3 directLightColor_Diffuse;\\n#if NUM_POINT_LIGHTS > 0\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_SPOT_LIGHTS > 0\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_DIR_LIGHTS > 0\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_HEMI_LIGHTS > 0\\n\\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\\n\\t\\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n\tvar lights_pars = \"uniform vec3 ambientLightColor;\\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\\n\\tvec3 irradiance = ambientLightColor;\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\treturn irradiance;\\n}\\n#if NUM_DIR_LIGHTS > 0\\n\\tstruct DirectionalLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t};\\n\\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\\n\\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tdirectLight.color = directionalLight.color;\\n\\t\\tdirectLight.direction = directionalLight.direction;\\n\\t\\tdirectLight.visible = true;\\n\\t}\\n#endif\\n#if NUM_POINT_LIGHTS > 0\\n\\tstruct PointLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t};\\n\\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\\n\\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tvec3 lVector = pointLight.position - geometry.position;\\n\\t\\tdirectLight.direction = normalize( lVector );\\n\\t\\tfloat lightDistance = length( lVector );\\n\\t\\tif ( testLightInRange( lightDistance, pointLight.distance ) ) {\\n\\t\\t\\tdirectLight.color = pointLight.color;\\n\\t\\t\\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\\n\\t\\t\\tdirectLight.visible = true;\\n\\t\\t} else {\\n\\t\\t\\tdirectLight.color = vec3( 0.0 );\\n\\t\\t\\tdirectLight.visible = false;\\n\\t\\t}\\n\\t}\\n#endif\\n#if NUM_SPOT_LIGHTS > 0\\n\\tstruct SpotLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t\\tfloat coneCos;\\n\\t\\tfloat penumbraCos;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t};\\n\\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\\n\\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tvec3 lVector = spotLight.position - geometry.position;\\n\\t\\tdirectLight.direction = normalize( lVector );\\n\\t\\tfloat lightDistance = length( lVector );\\n\\t\\tfloat angleCos = dot( directLight.direction, spotLight.direction );\\n\\t\\tif ( all( bvec2( angleCos > spotLight.coneCos, testLightInRange( lightDistance, spotLight.distance ) ) ) ) {\\n\\t\\t\\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\\n\\t\\t\\tdirectLight.color = spotLight.color;\\n\\t\\t\\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\\n\\t\\t\\tdirectLight.visible = true;\\n\\t\\t} else {\\n\\t\\t\\tdirectLight.color = vec3( 0.0 );\\n\\t\\t\\tdirectLight.visible = false;\\n\\t\\t}\\n\\t}\\n#endif\\n#if NUM_HEMI_LIGHTS > 0\\n\\tstruct HemisphereLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 skyColor;\\n\\t\\tvec3 groundColor;\\n\\t};\\n\\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\\n\\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\\n\\t\\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\\n\\t\\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\\n\\t\\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\\n\\t\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\t\\tirradiance *= PI;\\n\\t\\t#endif\\n\\t\\treturn irradiance;\\n\\t}\\n#endif\\n#if defined( USE_ENVMAP ) && defined( PHYSICAL )\\n\\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\\n\\t\\t#include <normal_flip>\\n\\t\\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\t\\tvec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\t\\tvec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\\n\\t\\t#else\\n\\t\\t\\tvec4 envMapColor = vec4( 0.0 );\\n\\t\\t#endif\\n\\t\\treturn PI * envMapColor.rgb * envMapIntensity;\\n\\t}\\n\\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\\n\\t\\tfloat maxMIPLevelScalar = float( maxMIPLevel );\\n\\t\\tfloat desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\\n\\t\\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\\n\\t}\\n\\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\\n\\t\\t#else\\n\\t\\t\\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\\n\\t\\t#endif\\n\\t\\t#include <normal_flip>\\n\\t\\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\\n\\t\\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\t\\tvec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\t\\tvec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\\n\\t\\t#elif defined( ENVMAP_TYPE_EQUIREC )\\n\\t\\t\\tvec2 sampleUV;\\n\\t\\t\\tsampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\\n\\t\\t\\tsampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_SPHERE )\\n\\t\\t\\tvec3 reflectView = flipNormal * normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#endif\\n\\t\\treturn envMapColor.rgb * envMapIntensity;\\n\\t}\\n#endif\\n\";\n\n\tvar lights_phong_fragment = \"BlinnPhongMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb;\\nmaterial.specularColor = specular;\\nmaterial.specularShininess = shininess;\\nmaterial.specularStrength = specularStrength;\\n\";\n\n\tvar lights_phong_pars_fragment = \"varying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\nstruct BlinnPhongMaterial {\\n\\tvec3\\tdiffuseColor;\\n\\tvec3\\tspecularColor;\\n\\tfloat\\tspecularShininess;\\n\\tfloat\\tspecularStrength;\\n};\\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\\n\\tvec3 irradiance = dotNL * directLight.color;\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n\\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\\n}\\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_BlinnPhong\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_BlinnPhong\\n#define Material_LightProbeLOD( material )\\t(0)\\n\";\n\n\tvar lights_physical_fragment = \"PhysicalMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\\n#ifdef STANDARD\\n\\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\\n#else\\n\\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\\n\\tmaterial.clearCoat = saturate( clearCoat );\\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\\n#endif\\n\";\n\n\tvar lights_physical_pars_fragment = \"struct PhysicalMaterial {\\n\\tvec3\\tdiffuseColor;\\n\\tfloat\\tspecularRoughness;\\n\\tvec3\\tspecularColor;\\n\\t#ifndef STANDARD\\n\\t\\tfloat clearCoat;\\n\\t\\tfloat clearCoatRoughness;\\n\\t#endif\\n};\\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\\n\\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\\n}\\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\\n\\tvec3 irradiance = dotNL * directLight.color;\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\t#ifndef STANDARD\\n\\t\\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\\n\\t#else\\n\\t\\tfloat clearCoatDHR = 0.0;\\n\\t#endif\\n\\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\\n\\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n\\t#ifndef STANDARD\\n\\t\\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\\n\\t#endif\\n}\\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n}\\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t#ifndef STANDARD\\n\\t\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\t\\tfloat dotNL = dotNV;\\n\\t\\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\\n\\t#else\\n\\t\\tfloat clearCoatDHR = 0.0;\\n\\t#endif\\n\\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\\n\\t#ifndef STANDARD\\n\\t\\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\\n\\t#endif\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_Physical\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_Physical\\n#define RE_IndirectSpecular\\t\\tRE_IndirectSpecular_Physical\\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\\n\\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\\n}\\n\";\n\n\tvar lights_template = \"\\nGeometricContext geometry;\\ngeometry.position = - vViewPosition;\\ngeometry.normal = normal;\\ngeometry.viewDir = normalize( vViewPosition );\\nIncidentLight directLight;\\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tPointLight pointLight;\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tpointLight = pointLights[ i ];\\n\\t\\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tSpotLight spotLight;\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tspotLight = spotLights[ i ];\\n\\t\\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tDirectionalLight directionalLight;\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLights[ i ];\\n\\t\\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if defined( RE_IndirectDiffuse )\\n\\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\\n\\t#ifdef USE_LIGHTMAP\\n\\t\\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n\\t\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\t\\tlightMapIrradiance *= PI;\\n\\t\\t#endif\\n\\t\\tirradiance += lightMapIrradiance;\\n\\t#endif\\n\\t#if ( NUM_HEMI_LIGHTS > 0 )\\n\\t\\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\\n\\t\\t\\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\\n\\t\\t}\\n\\t#endif\\n\\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\\n\\t \\tirradiance += getLightProbeIndirectIrradiance( geometry, 8 );\\n\\t#endif\\n\\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\\n#endif\\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\\n\\tvec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\\n\\t#ifndef STANDARD\\n\\t\\tvec3 clearCoatRadiance = getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 );\\n\\t#else\\n\\t\\tvec3 clearCoatRadiance = vec3( 0.0 );\\n\\t#endif\\n\\t\\t\\n\\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\\n#endif\\n\";\n\n\tvar logdepthbuf_fragment = \"#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\\n\\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\\n#endif\";\n\n\tvar logdepthbuf_pars_fragment = \"#ifdef USE_LOGDEPTHBUF\\n\\tuniform float logDepthBufFC;\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvarying float vFragDepth;\\n\\t#endif\\n#endif\\n\";\n\n\tvar logdepthbuf_pars_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvarying float vFragDepth;\\n\\t#endif\\n\\tuniform float logDepthBufFC;\\n#endif\";\n\n\tvar logdepthbuf_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\tgl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvFragDepth = 1.0 + gl_Position.w;\\n\\t#else\\n\\t\\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\\n\\t#endif\\n#endif\\n\";\n\n\tvar map_fragment = \"#ifdef USE_MAP\\n\\tvec4 texelColor = texture2D( map, vUv );\\n\\ttexelColor = mapTexelToLinear( texelColor );\\n\\tdiffuseColor *= texelColor;\\n#endif\\n\";\n\n\tvar map_pars_fragment = \"#ifdef USE_MAP\\n\\tuniform sampler2D map;\\n#endif\\n\";\n\n\tvar map_particle_fragment = \"#ifdef USE_MAP\\n\\tvec4 mapTexel = texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\\n\\tdiffuseColor *= mapTexelToLinear( mapTexel );\\n#endif\\n\";\n\n\tvar map_particle_pars_fragment = \"#ifdef USE_MAP\\n\\tuniform vec4 offsetRepeat;\\n\\tuniform sampler2D map;\\n#endif\\n\";\n\n\tvar metalnessmap_fragment = \"float metalnessFactor = metalness;\\n#ifdef USE_METALNESSMAP\\n\\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\\n\\tmetalnessFactor *= texelMetalness.r;\\n#endif\\n\";\n\n\tvar metalnessmap_pars_fragment = \"#ifdef USE_METALNESSMAP\\n\\tuniform sampler2D metalnessMap;\\n#endif\";\n\n\tvar morphnormal_vertex = \"#ifdef USE_MORPHNORMALS\\n\\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\\n\\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\\n\\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\\n\\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\\n#endif\\n\";\n\n\tvar morphtarget_pars_vertex = \"#ifdef USE_MORPHTARGETS\\n\\t#ifndef USE_MORPHNORMALS\\n\\tuniform float morphTargetInfluences[ 8 ];\\n\\t#else\\n\\tuniform float morphTargetInfluences[ 4 ];\\n\\t#endif\\n#endif\";\n\n\tvar morphtarget_vertex = \"#ifdef USE_MORPHTARGETS\\n\\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\\n\\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\\n\\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\\n\\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\\n\\t#ifndef USE_MORPHNORMALS\\n\\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\\n\\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\\n\\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\\n\\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\\n\\t#endif\\n#endif\\n\";\n\n\tvar normal_flip = \"#ifdef DOUBLE_SIDED\\n\\tfloat flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\\n#else\\n\\tfloat flipNormal = 1.0;\\n#endif\\n\";\n\n\tvar normal_fragment = \"#ifdef FLAT_SHADED\\n\\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\\n\\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\\n\\tvec3 normal = normalize( cross( fdx, fdy ) );\\n#else\\n\\tvec3 normal = normalize( vNormal ) * flipNormal;\\n#endif\\n#ifdef USE_NORMALMAP\\n\\tnormal = perturbNormal2Arb( -vViewPosition, normal );\\n#elif defined( USE_BUMPMAP )\\n\\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\\n#endif\\n\";\n\n\tvar normalmap_pars_fragment = \"#ifdef USE_NORMALMAP\\n\\tuniform sampler2D normalMap;\\n\\tuniform vec2 normalScale;\\n\\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\\n\\t\\tvec3 q0 = dFdx( eye_pos.xyz );\\n\\t\\tvec3 q1 = dFdy( eye_pos.xyz );\\n\\t\\tvec2 st0 = dFdx( vUv.st );\\n\\t\\tvec2 st1 = dFdy( vUv.st );\\n\\t\\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\\n\\t\\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\\n\\t\\tvec3 N = normalize( surf_norm );\\n\\t\\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\\n\\t\\tmapN.xy = normalScale * mapN.xy;\\n\\t\\tmat3 tsn = mat3( S, T, N );\\n\\t\\treturn normalize( tsn * mapN );\\n\\t}\\n#endif\\n\";\n\n\tvar packing = \"vec3 packNormalToRGB( const in vec3 normal ) {\\n return normalize( normal ) * 0.5 + 0.5;\\n}\\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\\n return 1.0 - 2.0 * rgb.xyz;\\n}\\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\\nconst float ShiftRight8 = 1. / 256.;\\nvec4 packDepthToRGBA( const in float v ) {\\n\\tvec4 r = vec4( fract( v * PackFactors ), v );\\n\\tr.yzw -= r.xyz * ShiftRight8;\\treturn r * PackUpscale;\\n}\\nfloat unpackRGBAToDepth( const in vec4 v ) {\\n\\treturn dot( v, UnpackFactors );\\n}\\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\\n return ( viewZ + near ) / ( near - far );\\n}\\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\\n return linearClipZ * ( near - far ) - near;\\n}\\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\\n return (( near + viewZ ) * far ) / (( far - near ) * viewZ );\\n}\\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\\n return ( near * far ) / ( ( far - near ) * invClipZ - far );\\n}\\n\";\n\n\tvar premultiplied_alpha_fragment = \"#ifdef PREMULTIPLIED_ALPHA\\n\\tgl_FragColor.rgb *= gl_FragColor.a;\\n#endif\\n\";\n\n\tvar project_vertex = \"#ifdef USE_SKINNING\\n\\tvec4 mvPosition = modelViewMatrix * skinned;\\n#else\\n\\tvec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\\n#endif\\ngl_Position = projectionMatrix * mvPosition;\\n\";\n\n\tvar roughnessmap_fragment = \"float roughnessFactor = roughness;\\n#ifdef USE_ROUGHNESSMAP\\n\\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\\n\\troughnessFactor *= texelRoughness.r;\\n#endif\\n\";\n\n\tvar roughnessmap_pars_fragment = \"#ifdef USE_ROUGHNESSMAP\\n\\tuniform sampler2D roughnessMap;\\n#endif\";\n\n\tvar shadowmap_pars_fragment = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t\\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t\\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\\n\\t\\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t\\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\\n\\t#endif\\n\\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\\n\\t\\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\\n\\t}\\n\\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\\n\\t\\tconst vec2 offset = vec2( 0.0, 1.0 );\\n\\t\\tvec2 texelSize = vec2( 1.0 ) / size;\\n\\t\\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\\n\\t\\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\\n\\t\\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\\n\\t\\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\\n\\t\\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\\n\\t\\tvec2 f = fract( uv * size + 0.5 );\\n\\t\\tfloat a = mix( lb, lt, f.y );\\n\\t\\tfloat b = mix( rb, rt, f.y );\\n\\t\\tfloat c = mix( a, b, f.x );\\n\\t\\treturn c;\\n\\t}\\n\\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\\n\\t\\tshadowCoord.xyz /= shadowCoord.w;\\n\\t\\tshadowCoord.z += shadowBias;\\n\\t\\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\\n\\t\\tbool inFrustum = all( inFrustumVec );\\n\\t\\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\\n\\t\\tbool frustumTest = all( frustumTestVec );\\n\\t\\tif ( frustumTest ) {\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx0 = - texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy0 = - texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx1 = + texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy1 = + texelSize.y * shadowRadius;\\n\\t\\t\\treturn (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx0 = - texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy0 = - texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx1 = + texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy1 = + texelSize.y * shadowRadius;\\n\\t\\t\\treturn (\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#else\\n\\t\\t\\treturn texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\\n\\t\\t#endif\\n\\t\\t}\\n\\t\\treturn 1.0;\\n\\t}\\n\\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\\n\\t\\tvec3 absV = abs( v );\\n\\t\\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\\n\\t\\tabsV *= scaleToCube;\\n\\t\\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\\n\\t\\tvec2 planar = v.xy;\\n\\t\\tfloat almostATexel = 1.5 * texelSizeY;\\n\\t\\tfloat almostOne = 1.0 - almostATexel;\\n\\t\\tif ( absV.z >= almostOne ) {\\n\\t\\t\\tif ( v.z > 0.0 )\\n\\t\\t\\t\\tplanar.x = 4.0 - v.x;\\n\\t\\t} else if ( absV.x >= almostOne ) {\\n\\t\\t\\tfloat signX = sign( v.x );\\n\\t\\t\\tplanar.x = v.z * signX + 2.0 * signX;\\n\\t\\t} else if ( absV.y >= almostOne ) {\\n\\t\\t\\tfloat signY = sign( v.y );\\n\\t\\t\\tplanar.x = v.x + 2.0 * signY + 2.0;\\n\\t\\t\\tplanar.y = v.z * signY - 2.0;\\n\\t\\t}\\n\\t\\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\\n\\t}\\n\\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\\n\\t\\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\\n\\t\\tvec3 lightToPosition = shadowCoord.xyz;\\n\\t\\tvec3 bd3D = normalize( lightToPosition );\\n\\t\\tfloat dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\\n\\t\\t\\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\\n\\t\\t\\treturn (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#else\\n\\t\\t\\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n\tvar shadowmap_pars_vertex = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t\\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t\\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\\n\\t\\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t\\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\\n\\t#endif\\n#endif\\n\";\n\n\tvar shadowmap_vertex = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n#endif\\n\";\n\n\tvar shadowmask_pars_fragment = \"float getShadowMask() {\\n\\tfloat shadow = 1.0;\\n\\t#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\tDirectionalLight directionalLight;\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLights[ i ];\\n\\t\\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\tSpotLight spotLight;\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tspotLight = spotLights[ i ];\\n\\t\\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\tPointLight pointLight;\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tpointLight = pointLights[ i ];\\n\\t\\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#endif\\n\\treturn shadow;\\n}\\n\";\n\n\tvar skinbase_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\\n\\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\\n\\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\\n\\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\\n#endif\";\n\n\tvar skinning_pars_vertex = \"#ifdef USE_SKINNING\\n\\tuniform mat4 bindMatrix;\\n\\tuniform mat4 bindMatrixInverse;\\n\\t#ifdef BONE_TEXTURE\\n\\t\\tuniform sampler2D boneTexture;\\n\\t\\tuniform int boneTextureWidth;\\n\\t\\tuniform int boneTextureHeight;\\n\\t\\tmat4 getBoneMatrix( const in float i ) {\\n\\t\\t\\tfloat j = i * 4.0;\\n\\t\\t\\tfloat x = mod( j, float( boneTextureWidth ) );\\n\\t\\t\\tfloat y = floor( j / float( boneTextureWidth ) );\\n\\t\\t\\tfloat dx = 1.0 / float( boneTextureWidth );\\n\\t\\t\\tfloat dy = 1.0 / float( boneTextureHeight );\\n\\t\\t\\ty = dy * ( y + 0.5 );\\n\\t\\t\\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\\n\\t\\t\\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\\n\\t\\t\\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\\n\\t\\t\\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\\n\\t\\t\\tmat4 bone = mat4( v1, v2, v3, v4 );\\n\\t\\t\\treturn bone;\\n\\t\\t}\\n\\t#else\\n\\t\\tuniform mat4 boneMatrices[ MAX_BONES ];\\n\\t\\tmat4 getBoneMatrix( const in float i ) {\\n\\t\\t\\tmat4 bone = boneMatrices[ int(i) ];\\n\\t\\t\\treturn bone;\\n\\t\\t}\\n\\t#endif\\n#endif\\n\";\n\n\tvar skinning_vertex = \"#ifdef USE_SKINNING\\n\\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\\n\\tvec4 skinned = vec4( 0.0 );\\n\\tskinned += boneMatX * skinVertex * skinWeight.x;\\n\\tskinned += boneMatY * skinVertex * skinWeight.y;\\n\\tskinned += boneMatZ * skinVertex * skinWeight.z;\\n\\tskinned += boneMatW * skinVertex * skinWeight.w;\\n\\tskinned = bindMatrixInverse * skinned;\\n#endif\\n\";\n\n\tvar skinnormal_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 skinMatrix = mat4( 0.0 );\\n\\tskinMatrix += skinWeight.x * boneMatX;\\n\\tskinMatrix += skinWeight.y * boneMatY;\\n\\tskinMatrix += skinWeight.z * boneMatZ;\\n\\tskinMatrix += skinWeight.w * boneMatW;\\n\\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\\n\\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\\n#endif\\n\";\n\n\tvar specularmap_fragment = \"float specularStrength;\\n#ifdef USE_SPECULARMAP\\n\\tvec4 texelSpecular = texture2D( specularMap, vUv );\\n\\tspecularStrength = texelSpecular.r;\\n#else\\n\\tspecularStrength = 1.0;\\n#endif\";\n\n\tvar specularmap_pars_fragment = \"#ifdef USE_SPECULARMAP\\n\\tuniform sampler2D specularMap;\\n#endif\";\n\n\tvar tonemapping_fragment = \"#if defined( TONE_MAPPING )\\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\\n#endif\\n\";\n\n\tvar tonemapping_pars_fragment = \"#define saturate(a) clamp( a, 0.0, 1.0 )\\nuniform float toneMappingExposure;\\nuniform float toneMappingWhitePoint;\\nvec3 LinearToneMapping( vec3 color ) {\\n return toneMappingExposure * color;\\n}\\nvec3 ReinhardToneMapping( vec3 color ) {\\n color *= toneMappingExposure;\\n return saturate( color / ( vec3( 1.0 ) + color ) );\\n}\\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\\nvec3 Uncharted2ToneMapping( vec3 color ) {\\n color *= toneMappingExposure;\\n return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\\n}\\nvec3 OptimizedCineonToneMapping( vec3 color ) {\\n color *= toneMappingExposure;\\n color = max( vec3( 0.0 ), color - 0.004 );\\n return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\\n}\\n\";\n\n\tvar uv_pars_fragment = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvarying vec2 vUv;\\n#endif\";\n\n\tvar uv_pars_vertex = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvarying vec2 vUv;\\n\\tuniform vec4 offsetRepeat;\\n#endif\\n\";\n\n\tvar uv_vertex = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\\n#endif\";\n\n\tvar uv2_pars_fragment = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tvarying vec2 vUv2;\\n#endif\";\n\n\tvar uv2_pars_vertex = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tattribute vec2 uv2;\\n\\tvarying vec2 vUv2;\\n#endif\";\n\n\tvar uv2_vertex = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tvUv2 = uv2;\\n#endif\";\n\n\tvar worldpos_vertex = \"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\\n\\t#ifdef USE_SKINNING\\n\\t\\tvec4 worldPosition = modelMatrix * skinned;\\n\\t#else\\n\\t\\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\\n\\t#endif\\n#endif\\n\";\n\n\tvar cube_frag = \"uniform samplerCube tCube;\\nuniform float tFlip;\\nuniform float opacity;\\nvarying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\\n\\tgl_FragColor.a *= opacity;\\n}\\n\";\n\n\tvar cube_vert = \"varying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvWorldPosition = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n}\\n\";\n\n\tvar depth_frag = \"#if DEPTH_PACKING == 3200\\n\\tuniform float opacity;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( 1.0 );\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tdiffuseColor.a = opacity;\\n\\t#endif\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <logdepthbuf_fragment>\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\\n\\t#elif DEPTH_PACKING == 3201\\n\\t\\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\\n\\t#endif\\n}\\n\";\n\n\tvar depth_vert = \"#include <common>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n}\\n\";\n\n\tvar distanceRGBA_frag = \"uniform vec3 lightPos;\\nvarying vec4 vWorldPosition;\\n#include <common>\\n#include <packing>\\n#include <clipping_planes_pars_fragment>\\nvoid main () {\\n\\t#include <clipping_planes_fragment>\\n\\tgl_FragColor = packDepthToRGBA( length( vWorldPosition.xyz - lightPos.xyz ) / 1000.0 );\\n}\\n\";\n\n\tvar distanceRGBA_vert = \"varying vec4 vWorldPosition;\\n#include <common>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <skinbase_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvWorldPosition = worldPosition;\\n}\\n\";\n\n\tvar equirect_frag = \"uniform sampler2D tEquirect;\\nuniform float tFlip;\\nvarying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvec3 direction = normalize( vWorldPosition );\\n\\tvec2 sampleUV;\\n\\tsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\\n\\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\\n\\tgl_FragColor = texture2D( tEquirect, sampleUV );\\n}\\n\";\n\n\tvar equirect_vert = \"varying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvWorldPosition = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n}\\n\";\n\n\tvar linedashed_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\nuniform float dashSize;\\nuniform float totalSize;\\nvarying float vLineDistance;\\n#include <common>\\n#include <color_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\\n\\t\\tdiscard;\\n\\t}\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <color_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n\tvar linedashed_vert = \"uniform float scale;\\nattribute float lineDistance;\\nvarying float vLineDistance;\\n#include <common>\\n#include <color_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <color_vertex>\\n\\tvLineDistance = scale * lineDistance;\\n\\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\\n\\tgl_Position = projectionMatrix * mvPosition;\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n}\\n\";\n\n\tvar meshbasic_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\tReflectedLight reflectedLight;\\n\\treflectedLight.directDiffuse = vec3( 0.0 );\\n\\treflectedLight.directSpecular = vec3( 0.0 );\\n\\treflectedLight.indirectDiffuse = diffuseColor.rgb;\\n\\treflectedLight.indirectSpecular = vec3( 0.0 );\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\\n\\t#include <normal_flip>\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n\tvar meshbasic_vert = \"#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_ENVMAP\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <envmap_vertex>\\n}\\n\";\n\n\tvar meshlambert_frag = \"uniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float opacity;\\nvarying vec3 vLightFront;\\n#ifdef DOUBLE_SIDED\\n\\tvarying vec3 vLightBack;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars>\\n#include <fog_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <shadowmask_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <emissivemap_fragment>\\n\\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\\n\\t#include <lightmap_fragment>\\n\\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\\n\\t#ifdef DOUBLE_SIDED\\n\\t\\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\\n\\t#else\\n\\t\\treflectedLight.directDiffuse = vLightFront;\\n\\t#endif\\n\\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\\n\\t#include <normal_flip>\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n\tvar meshlambert_vert = \"#define LAMBERT\\nvarying vec3 vLightFront;\\n#ifdef DOUBLE_SIDED\\n\\tvarying vec3 vLightBack;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <bsdfs>\\n#include <lights_pars>\\n#include <color_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <lights_lambert_vertex>\\n\\t#include <shadowmap_vertex>\\n}\\n\";\n\n\tvar meshphong_frag = \"#define PHONG\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform vec3 specular;\\nuniform float shininess;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars>\\n#include <lights_phong_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <normal_flip>\\n\\t#include <normal_fragment>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_phong_fragment>\\n\\t#include <lights_template>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n\tvar meshphong_vert = \"#define PHONG\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <shadowmap_vertex>\\n}\\n\";\n\n\tvar meshphysical_frag = \"#define PHYSICAL\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float roughness;\\nuniform float metalness;\\nuniform float opacity;\\n#ifndef STANDARD\\n\\tuniform float clearCoat;\\n\\tuniform float clearCoatRoughness;\\n#endif\\nuniform float envMapIntensity;\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <cube_uv_reflection_fragment>\\n#include <lights_pars>\\n#include <lights_physical_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <roughnessmap_pars_fragment>\\n#include <metalnessmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <roughnessmap_fragment>\\n\\t#include <metalnessmap_fragment>\\n\\t#include <normal_flip>\\n\\t#include <normal_fragment>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_physical_fragment>\\n\\t#include <lights_template>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n\tvar meshphysical_vert = \"#define PHYSICAL\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n}\\n\";\n\n\tvar normal_frag = \"uniform float opacity;\\nvarying vec3 vNormal;\\n#include <common>\\n#include <packing>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tgl_FragColor = vec4( packNormalToRGB( vNormal ), opacity );\\n\\t#include <logdepthbuf_fragment>\\n}\\n\";\n\n\tvar normal_vert = \"varying vec3 vNormal;\\n#include <common>\\n#include <morphtarget_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\tvNormal = normalize( normalMatrix * normal );\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n}\\n\";\n\n\tvar points_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <color_pars_fragment>\\n#include <map_particle_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_particle_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphatest_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n\tvar points_vert = \"uniform float size;\\nuniform float scale;\\n#include <common>\\n#include <color_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <color_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\t#ifdef USE_SIZEATTENUATION\\n\\t\\tgl_PointSize = size * ( scale / - mvPosition.z );\\n\\t#else\\n\\t\\tgl_PointSize = size;\\n\\t#endif\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n}\\n\";\n\n\tvar shadow_frag = \"uniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <bsdfs>\\n#include <lights_pars>\\n#include <shadowmap_pars_fragment>\\n#include <shadowmask_pars_fragment>\\nvoid main() {\\n\\tgl_FragColor = vec4( 0.0, 0.0, 0.0, opacity * ( 1.0 - getShadowMask() ) );\\n}\\n\";\n\n\tvar shadow_vert = \"#include <shadowmap_pars_vertex>\\nvoid main() {\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n}\\n\";\n\n\tvar ShaderChunk = {\n\t\talphamap_fragment: alphamap_fragment,\n\t\talphamap_pars_fragment: alphamap_pars_fragment,\n\t\talphatest_fragment: alphatest_fragment,\n\t\taomap_fragment: aomap_fragment,\n\t\taomap_pars_fragment: aomap_pars_fragment,\n\t\tbegin_vertex: begin_vertex,\n\t\tbeginnormal_vertex: beginnormal_vertex,\n\t\tbsdfs: bsdfs,\n\t\tbumpmap_pars_fragment: bumpmap_pars_fragment,\n\t\tclipping_planes_fragment: clipping_planes_fragment,\n\t\tclipping_planes_pars_fragment: clipping_planes_pars_fragment,\n\t\tclipping_planes_pars_vertex: clipping_planes_pars_vertex,\n\t\tclipping_planes_vertex: clipping_planes_vertex,\n\t\tcolor_fragment: color_fragment,\n\t\tcolor_pars_fragment: color_pars_fragment,\n\t\tcolor_pars_vertex: color_pars_vertex,\n\t\tcolor_vertex: color_vertex,\n\t\tcommon: common,\n\t\tcube_uv_reflection_fragment: cube_uv_reflection_fragment,\n\t\tdefaultnormal_vertex: defaultnormal_vertex,\n\t\tdisplacementmap_pars_vertex: displacementmap_pars_vertex,\n\t\tdisplacementmap_vertex: displacementmap_vertex,\n\t\temissivemap_fragment: emissivemap_fragment,\n\t\temissivemap_pars_fragment: emissivemap_pars_fragment,\n\t\tencodings_fragment: encodings_fragment,\n\t\tencodings_pars_fragment: encodings_pars_fragment,\n\t\tenvmap_fragment: envmap_fragment,\n\t\tenvmap_pars_fragment: envmap_pars_fragment,\n\t\tenvmap_pars_vertex: envmap_pars_vertex,\n\t\tenvmap_vertex: envmap_vertex,\n\t\tfog_fragment: fog_fragment,\n\t\tfog_pars_fragment: fog_pars_fragment,\n\t\tlightmap_fragment: lightmap_fragment,\n\t\tlightmap_pars_fragment: lightmap_pars_fragment,\n\t\tlights_lambert_vertex: lights_lambert_vertex,\n\t\tlights_pars: lights_pars,\n\t\tlights_phong_fragment: lights_phong_fragment,\n\t\tlights_phong_pars_fragment: lights_phong_pars_fragment,\n\t\tlights_physical_fragment: lights_physical_fragment,\n\t\tlights_physical_pars_fragment: lights_physical_pars_fragment,\n\t\tlights_template: lights_template,\n\t\tlogdepthbuf_fragment: logdepthbuf_fragment,\n\t\tlogdepthbuf_pars_fragment: logdepthbuf_pars_fragment,\n\t\tlogdepthbuf_pars_vertex: logdepthbuf_pars_vertex,\n\t\tlogdepthbuf_vertex: logdepthbuf_vertex,\n\t\tmap_fragment: map_fragment,\n\t\tmap_pars_fragment: map_pars_fragment,\n\t\tmap_particle_fragment: map_particle_fragment,\n\t\tmap_particle_pars_fragment: map_particle_pars_fragment,\n\t\tmetalnessmap_fragment: metalnessmap_fragment,\n\t\tmetalnessmap_pars_fragment: metalnessmap_pars_fragment,\n\t\tmorphnormal_vertex: morphnormal_vertex,\n\t\tmorphtarget_pars_vertex: morphtarget_pars_vertex,\n\t\tmorphtarget_vertex: morphtarget_vertex,\n\t\tnormal_flip: normal_flip,\n\t\tnormal_fragment: normal_fragment,\n\t\tnormalmap_pars_fragment: normalmap_pars_fragment,\n\t\tpacking: packing,\n\t\tpremultiplied_alpha_fragment: premultiplied_alpha_fragment,\n\t\tproject_vertex: project_vertex,\n\t\troughnessmap_fragment: roughnessmap_fragment,\n\t\troughnessmap_pars_fragment: roughnessmap_pars_fragment,\n\t\tshadowmap_pars_fragment: shadowmap_pars_fragment,\n\t\tshadowmap_pars_vertex: shadowmap_pars_vertex,\n\t\tshadowmap_vertex: shadowmap_vertex,\n\t\tshadowmask_pars_fragment: shadowmask_pars_fragment,\n\t\tskinbase_vertex: skinbase_vertex,\n\t\tskinning_pars_vertex: skinning_pars_vertex,\n\t\tskinning_vertex: skinning_vertex,\n\t\tskinnormal_vertex: skinnormal_vertex,\n\t\tspecularmap_fragment: specularmap_fragment,\n\t\tspecularmap_pars_fragment: specularmap_pars_fragment,\n\t\ttonemapping_fragment: tonemapping_fragment,\n\t\ttonemapping_pars_fragment: tonemapping_pars_fragment,\n\t\tuv_pars_fragment: uv_pars_fragment,\n\t\tuv_pars_vertex: uv_pars_vertex,\n\t\tuv_vertex: uv_vertex,\n\t\tuv2_pars_fragment: uv2_pars_fragment,\n\t\tuv2_pars_vertex: uv2_pars_vertex,\n\t\tuv2_vertex: uv2_vertex,\n\t\tworldpos_vertex: worldpos_vertex,\n\n\t\tcube_frag: cube_frag,\n\t\tcube_vert: cube_vert,\n\t\tdepth_frag: depth_frag,\n\t\tdepth_vert: depth_vert,\n\t\tdistanceRGBA_frag: distanceRGBA_frag,\n\t\tdistanceRGBA_vert: distanceRGBA_vert,\n\t\tequirect_frag: equirect_frag,\n\t\tequirect_vert: equirect_vert,\n\t\tlinedashed_frag: linedashed_frag,\n\t\tlinedashed_vert: linedashed_vert,\n\t\tmeshbasic_frag: meshbasic_frag,\n\t\tmeshbasic_vert: meshbasic_vert,\n\t\tmeshlambert_frag: meshlambert_frag,\n\t\tmeshlambert_vert: meshlambert_vert,\n\t\tmeshphong_frag: meshphong_frag,\n\t\tmeshphong_vert: meshphong_vert,\n\t\tmeshphysical_frag: meshphysical_frag,\n\t\tmeshphysical_vert: meshphysical_vert,\n\t\tnormal_frag: normal_frag,\n\t\tnormal_vert: normal_vert,\n\t\tpoints_frag: points_frag,\n\t\tpoints_vert: points_vert,\n\t\tshadow_frag: shadow_frag,\n\t\tshadow_vert: shadow_vert\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction Color( r, g, b ) {\n\n\t\tif ( g === undefined && b === undefined ) {\n\n\t\t\t// r is THREE.Color, hex or string\n\t\t\treturn this.set( r );\n\n\t\t}\n\n\t\treturn this.setRGB( r, g, b );\n\n\t}\n\n\tColor.prototype = {\n\n\t\tconstructor: Color,\n\n\t\tisColor: true,\n\n\t\tr: 1, g: 1, b: 1,\n\n\t\tset: function ( value ) {\n\n\t\t\tif ( (value && value.isColor) ) {\n\n\t\t\t\tthis.copy( value );\n\n\t\t\t} else if ( typeof value === 'number' ) {\n\n\t\t\t\tthis.setHex( value );\n\n\t\t\t} else if ( typeof value === 'string' ) {\n\n\t\t\t\tthis.setStyle( value );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetScalar: function ( scalar ) {\n\n\t\t\tthis.r = scalar;\n\t\t\tthis.g = scalar;\n\t\t\tthis.b = scalar;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetHex: function ( hex ) {\n\n\t\t\thex = Math.floor( hex );\n\n\t\t\tthis.r = ( hex >> 16 & 255 ) / 255;\n\t\t\tthis.g = ( hex >> 8 & 255 ) / 255;\n\t\t\tthis.b = ( hex & 255 ) / 255;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetRGB: function ( r, g, b ) {\n\n\t\t\tthis.r = r;\n\t\t\tthis.g = g;\n\t\t\tthis.b = b;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetHSL: function () {\n\n\t\t\tfunction hue2rgb( p, q, t ) {\n\n\t\t\t\tif ( t < 0 ) t += 1;\n\t\t\t\tif ( t > 1 ) t -= 1;\n\t\t\t\tif ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;\n\t\t\t\tif ( t < 1 / 2 ) return q;\n\t\t\t\tif ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );\n\t\t\t\treturn p;\n\n\t\t\t}\n\n\t\t\treturn function setHSL( h, s, l ) {\n\n\t\t\t\t// h,s,l ranges are in 0.0 - 1.0\n\t\t\t\th = _Math.euclideanModulo( h, 1 );\n\t\t\t\ts = _Math.clamp( s, 0, 1 );\n\t\t\t\tl = _Math.clamp( l, 0, 1 );\n\n\t\t\t\tif ( s === 0 ) {\n\n\t\t\t\t\tthis.r = this.g = this.b = l;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tvar p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );\n\t\t\t\t\tvar q = ( 2 * l ) - p;\n\n\t\t\t\t\tthis.r = hue2rgb( q, p, h + 1 / 3 );\n\t\t\t\t\tthis.g = hue2rgb( q, p, h );\n\t\t\t\t\tthis.b = hue2rgb( q, p, h - 1 / 3 );\n\n\t\t\t\t}\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tsetStyle: function ( style ) {\n\n\t\t\tfunction handleAlpha( string ) {\n\n\t\t\t\tif ( string === undefined ) return;\n\n\t\t\t\tif ( parseFloat( string ) < 1 ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\n\t\t\tvar m;\n\n\t\t\tif ( m = /^((?:rgb|hsl)a?)\\(\\s*([^\\)]*)\\)/.exec( style ) ) {\n\n\t\t\t\t// rgb / hsl\n\n\t\t\t\tvar color;\n\t\t\t\tvar name = m[ 1 ];\n\t\t\t\tvar components = m[ 2 ];\n\n\t\t\t\tswitch ( name ) {\n\n\t\t\t\t\tcase 'rgb':\n\t\t\t\t\tcase 'rgba':\n\n\t\t\t\t\t\tif ( color = /^(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t\t// rgb(255,0,0) rgba(255,0,0,0.5)\n\t\t\t\t\t\t\tthis.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;\n\t\t\t\t\t\t\tthis.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;\n\t\t\t\t\t\t\tthis.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;\n\n\t\t\t\t\t\t\thandleAlpha( color[ 5 ] );\n\n\t\t\t\t\t\t\treturn this;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( color = /^(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t\t// rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)\n\t\t\t\t\t\t\tthis.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;\n\t\t\t\t\t\t\tthis.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;\n\t\t\t\t\t\t\tthis.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;\n\n\t\t\t\t\t\t\thandleAlpha( color[ 5 ] );\n\n\t\t\t\t\t\t\treturn this;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'hsl':\n\t\t\t\t\tcase 'hsla':\n\n\t\t\t\t\t\tif ( color = /^([0-9]*\\.?[0-9]+)\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t\t// hsl(120,50%,50%) hsla(120,50%,50%,0.5)\n\t\t\t\t\t\t\tvar h = parseFloat( color[ 1 ] ) / 360;\n\t\t\t\t\t\t\tvar s = parseInt( color[ 2 ], 10 ) / 100;\n\t\t\t\t\t\t\tvar l = parseInt( color[ 3 ], 10 ) / 100;\n\n\t\t\t\t\t\t\thandleAlpha( color[ 5 ] );\n\n\t\t\t\t\t\t\treturn this.setHSL( h, s, l );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t} else if ( m = /^\\#([A-Fa-f0-9]+)$/.exec( style ) ) {\n\n\t\t\t\t// hex color\n\n\t\t\t\tvar hex = m[ 1 ];\n\t\t\t\tvar size = hex.length;\n\n\t\t\t\tif ( size === 3 ) {\n\n\t\t\t\t\t// #ff0\n\t\t\t\t\tthis.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;\n\t\t\t\t\tthis.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;\n\t\t\t\t\tthis.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;\n\n\t\t\t\t\treturn this;\n\n\t\t\t\t} else if ( size === 6 ) {\n\n\t\t\t\t\t// #ff0000\n\t\t\t\t\tthis.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;\n\t\t\t\t\tthis.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;\n\t\t\t\t\tthis.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;\n\n\t\t\t\t\treturn this;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( style && style.length > 0 ) {\n\n\t\t\t\t// color keywords\n\t\t\t\tvar hex = ColorKeywords[ style ];\n\n\t\t\t\tif ( hex !== undefined ) {\n\n\t\t\t\t\t// red\n\t\t\t\t\tthis.setHex( hex );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// unknown color\n\t\t\t\t\tconsole.warn( 'THREE.Color: Unknown color ' + style );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.r, this.g, this.b );\n\n\t\t},\n\n\t\tcopy: function ( color ) {\n\n\t\t\tthis.r = color.r;\n\t\t\tthis.g = color.g;\n\t\t\tthis.b = color.b;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyGammaToLinear: function ( color, gammaFactor ) {\n\n\t\t\tif ( gammaFactor === undefined ) gammaFactor = 2.0;\n\n\t\t\tthis.r = Math.pow( color.r, gammaFactor );\n\t\t\tthis.g = Math.pow( color.g, gammaFactor );\n\t\t\tthis.b = Math.pow( color.b, gammaFactor );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyLinearToGamma: function ( color, gammaFactor ) {\n\n\t\t\tif ( gammaFactor === undefined ) gammaFactor = 2.0;\n\n\t\t\tvar safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;\n\n\t\t\tthis.r = Math.pow( color.r, safeInverse );\n\t\t\tthis.g = Math.pow( color.g, safeInverse );\n\t\t\tthis.b = Math.pow( color.b, safeInverse );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tconvertGammaToLinear: function () {\n\n\t\t\tvar r = this.r, g = this.g, b = this.b;\n\n\t\t\tthis.r = r * r;\n\t\t\tthis.g = g * g;\n\t\t\tthis.b = b * b;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tconvertLinearToGamma: function () {\n\n\t\t\tthis.r = Math.sqrt( this.r );\n\t\t\tthis.g = Math.sqrt( this.g );\n\t\t\tthis.b = Math.sqrt( this.b );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetHex: function () {\n\n\t\t\treturn ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;\n\n\t\t},\n\n\t\tgetHexString: function () {\n\n\t\t\treturn ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );\n\n\t\t},\n\n\t\tgetHSL: function ( optionalTarget ) {\n\n\t\t\t// h,s,l ranges are in 0.0 - 1.0\n\n\t\t\tvar hsl = optionalTarget || { h: 0, s: 0, l: 0 };\n\n\t\t\tvar r = this.r, g = this.g, b = this.b;\n\n\t\t\tvar max = Math.max( r, g, b );\n\t\t\tvar min = Math.min( r, g, b );\n\n\t\t\tvar hue, saturation;\n\t\t\tvar lightness = ( min + max ) / 2.0;\n\n\t\t\tif ( min === max ) {\n\n\t\t\t\thue = 0;\n\t\t\t\tsaturation = 0;\n\n\t\t\t} else {\n\n\t\t\t\tvar delta = max - min;\n\n\t\t\t\tsaturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );\n\n\t\t\t\tswitch ( max ) {\n\n\t\t\t\t\tcase r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;\n\t\t\t\t\tcase g: hue = ( b - r ) / delta + 2; break;\n\t\t\t\t\tcase b: hue = ( r - g ) / delta + 4; break;\n\n\t\t\t\t}\n\n\t\t\t\thue /= 6;\n\n\t\t\t}\n\n\t\t\thsl.h = hue;\n\t\t\thsl.s = saturation;\n\t\t\thsl.l = lightness;\n\n\t\t\treturn hsl;\n\n\t\t},\n\n\t\tgetStyle: function () {\n\n\t\t\treturn 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';\n\n\t\t},\n\n\t\toffsetHSL: function ( h, s, l ) {\n\n\t\t\tvar hsl = this.getHSL();\n\n\t\t\thsl.h += h; hsl.s += s; hsl.l += l;\n\n\t\t\tthis.setHSL( hsl.h, hsl.s, hsl.l );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tadd: function ( color ) {\n\n\t\t\tthis.r += color.r;\n\t\t\tthis.g += color.g;\n\t\t\tthis.b += color.b;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddColors: function ( color1, color2 ) {\n\n\t\t\tthis.r = color1.r + color2.r;\n\t\t\tthis.g = color1.g + color2.g;\n\t\t\tthis.b = color1.b + color2.b;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddScalar: function ( s ) {\n\n\t\t\tthis.r += s;\n\t\t\tthis.g += s;\n\t\t\tthis.b += s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsub: function( color ) {\n\n\t\t\tthis.r = Math.max( 0, this.r - color.r );\n\t\t\tthis.g = Math.max( 0, this.g - color.g );\n\t\t\tthis.b = Math.max( 0, this.b - color.b );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiply: function ( color ) {\n\n\t\t\tthis.r *= color.r;\n\t\t\tthis.g *= color.g;\n\t\t\tthis.b *= color.b;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmultiplyScalar: function ( s ) {\n\n\t\t\tthis.r *= s;\n\t\t\tthis.g *= s;\n\t\t\tthis.b *= s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tlerp: function ( color, alpha ) {\n\n\t\t\tthis.r += ( color.r - this.r ) * alpha;\n\t\t\tthis.g += ( color.g - this.g ) * alpha;\n\t\t\tthis.b += ( color.b - this.b ) * alpha;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( c ) {\n\n\t\t\treturn ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );\n\n\t\t},\n\n\t\tfromArray: function ( array, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tthis.r = array[ offset ];\n\t\t\tthis.g = array[ offset + 1 ];\n\t\t\tthis.b = array[ offset + 2 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tarray[ offset ] = this.r;\n\t\t\tarray[ offset + 1 ] = this.g;\n\t\t\tarray[ offset + 2 ] = this.b;\n\n\t\t\treturn array;\n\n\t\t},\n\n\t\ttoJSON: function () {\n\n\t\t\treturn this.getHex();\n\n\t\t}\n\n\t};\n\n\tvar ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,\n\t'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,\n\t'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,\n\t'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,\n\t'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,\n\t'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,\n\t'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,\n\t'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,\n\t'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,\n\t'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,\n\t'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,\n\t'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,\n\t'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,\n\t'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,\n\t'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,\n\t'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,\n\t'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,\n\t'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,\n\t'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,\n\t'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,\n\t'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,\n\t'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,\n\t'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,\n\t'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };\n\n\t/**\n\t * Uniforms library for shared webgl shaders\n\t */\n\n\tvar UniformsLib = {\n\n\t\tcommon: {\n\n\t\t\tdiffuse: { value: new Color( 0xeeeeee ) },\n\t\t\topacity: { value: 1.0 },\n\n\t\t\tmap: { value: null },\n\t\t\toffsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) },\n\n\t\t\tspecularMap: { value: null },\n\t\t\talphaMap: { value: null },\n\n\t\t\tenvMap: { value: null },\n\t\t\tflipEnvMap: { value: - 1 },\n\t\t\treflectivity: { value: 1.0 },\n\t\t\trefractionRatio: { value: 0.98 }\n\n\t\t},\n\n\t\taomap: {\n\n\t\t\taoMap: { value: null },\n\t\t\taoMapIntensity: { value: 1 }\n\n\t\t},\n\n\t\tlightmap: {\n\n\t\t\tlightMap: { value: null },\n\t\t\tlightMapIntensity: { value: 1 }\n\n\t\t},\n\n\t\temissivemap: {\n\n\t\t\temissiveMap: { value: null }\n\n\t\t},\n\n\t\tbumpmap: {\n\n\t\t\tbumpMap: { value: null },\n\t\t\tbumpScale: { value: 1 }\n\n\t\t},\n\n\t\tnormalmap: {\n\n\t\t\tnormalMap: { value: null },\n\t\t\tnormalScale: { value: new Vector2( 1, 1 ) }\n\n\t\t},\n\n\t\tdisplacementmap: {\n\n\t\t\tdisplacementMap: { value: null },\n\t\t\tdisplacementScale: { value: 1 },\n\t\t\tdisplacementBias: { value: 0 }\n\n\t\t},\n\n\t\troughnessmap: {\n\n\t\t\troughnessMap: { value: null }\n\n\t\t},\n\n\t\tmetalnessmap: {\n\n\t\t\tmetalnessMap: { value: null }\n\n\t\t},\n\n\t\tfog: {\n\n\t\t\tfogDensity: { value: 0.00025 },\n\t\t\tfogNear: { value: 1 },\n\t\t\tfogFar: { value: 2000 },\n\t\t\tfogColor: { value: new Color( 0xffffff ) }\n\n\t\t},\n\n\t\tlights: {\n\n\t\t\tambientLightColor: { value: [] },\n\n\t\t\tdirectionalLights: { value: [], properties: {\n\t\t\t\tdirection: {},\n\t\t\t\tcolor: {},\n\n\t\t\t\tshadow: {},\n\t\t\t\tshadowBias: {},\n\t\t\t\tshadowRadius: {},\n\t\t\t\tshadowMapSize: {}\n\t\t\t} },\n\n\t\t\tdirectionalShadowMap: { value: [] },\n\t\t\tdirectionalShadowMatrix: { value: [] },\n\n\t\t\tspotLights: { value: [], properties: {\n\t\t\t\tcolor: {},\n\t\t\t\tposition: {},\n\t\t\t\tdirection: {},\n\t\t\t\tdistance: {},\n\t\t\t\tconeCos: {},\n\t\t\t\tpenumbraCos: {},\n\t\t\t\tdecay: {},\n\n\t\t\t\tshadow: {},\n\t\t\t\tshadowBias: {},\n\t\t\t\tshadowRadius: {},\n\t\t\t\tshadowMapSize: {}\n\t\t\t} },\n\n\t\t\tspotShadowMap: { value: [] },\n\t\t\tspotShadowMatrix: { value: [] },\n\n\t\t\tpointLights: { value: [], properties: {\n\t\t\t\tcolor: {},\n\t\t\t\tposition: {},\n\t\t\t\tdecay: {},\n\t\t\t\tdistance: {},\n\n\t\t\t\tshadow: {},\n\t\t\t\tshadowBias: {},\n\t\t\t\tshadowRadius: {},\n\t\t\t\tshadowMapSize: {}\n\t\t\t} },\n\n\t\t\tpointShadowMap: { value: [] },\n\t\t\tpointShadowMatrix: { value: [] },\n\n\t\t\themisphereLights: { value: [], properties: {\n\t\t\t\tdirection: {},\n\t\t\t\tskyColor: {},\n\t\t\t\tgroundColor: {}\n\t\t\t} }\n\n\t\t},\n\n\t\tpoints: {\n\n\t\t\tdiffuse: { value: new Color( 0xeeeeee ) },\n\t\t\topacity: { value: 1.0 },\n\t\t\tsize: { value: 1.0 },\n\t\t\tscale: { value: 1.0 },\n\t\t\tmap: { value: null },\n\t\t\toffsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) }\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author mikael emtinger / http://gomo.se/\n\t */\n\n\tvar ShaderLib = {\n\n\t\tbasic: {\n\n\t\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\t\tUniformsLib.common,\n\t\t\t\tUniformsLib.aomap,\n\t\t\t\tUniformsLib.fog\n\n\t\t\t] ),\n\n\t\t\tvertexShader: ShaderChunk.meshbasic_vert,\n\t\t\tfragmentShader: ShaderChunk.meshbasic_frag\n\n\t\t},\n\n\t\tlambert: {\n\n\t\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\t\tUniformsLib.common,\n\t\t\t\tUniformsLib.aomap,\n\t\t\t\tUniformsLib.lightmap,\n\t\t\t\tUniformsLib.emissivemap,\n\t\t\t\tUniformsLib.fog,\n\t\t\t\tUniformsLib.lights,\n\n\t\t\t\t{\n\t\t\t\t\temissive : { value: new Color( 0x000000 ) }\n\t\t\t\t}\n\n\t\t\t] ),\n\n\t\t\tvertexShader: ShaderChunk.meshlambert_vert,\n\t\t\tfragmentShader: ShaderChunk.meshlambert_frag\n\n\t\t},\n\n\t\tphong: {\n\n\t\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\t\tUniformsLib.common,\n\t\t\t\tUniformsLib.aomap,\n\t\t\t\tUniformsLib.lightmap,\n\t\t\t\tUniformsLib.emissivemap,\n\t\t\t\tUniformsLib.bumpmap,\n\t\t\t\tUniformsLib.normalmap,\n\t\t\t\tUniformsLib.displacementmap,\n\t\t\t\tUniformsLib.fog,\n\t\t\t\tUniformsLib.lights,\n\n\t\t\t\t{\n\t\t\t\t\temissive : { value: new Color( 0x000000 ) },\n\t\t\t\t\tspecular : { value: new Color( 0x111111 ) },\n\t\t\t\t\tshininess: { value: 30 }\n\t\t\t\t}\n\n\t\t\t] ),\n\n\t\t\tvertexShader: ShaderChunk.meshphong_vert,\n\t\t\tfragmentShader: ShaderChunk.meshphong_frag\n\n\t\t},\n\n\t\tstandard: {\n\n\t\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\t\tUniformsLib.common,\n\t\t\t\tUniformsLib.aomap,\n\t\t\t\tUniformsLib.lightmap,\n\t\t\t\tUniformsLib.emissivemap,\n\t\t\t\tUniformsLib.bumpmap,\n\t\t\t\tUniformsLib.normalmap,\n\t\t\t\tUniformsLib.displacementmap,\n\t\t\t\tUniformsLib.roughnessmap,\n\t\t\t\tUniformsLib.metalnessmap,\n\t\t\t\tUniformsLib.fog,\n\t\t\t\tUniformsLib.lights,\n\n\t\t\t\t{\n\t\t\t\t\temissive : { value: new Color( 0x000000 ) },\n\t\t\t\t\troughness: { value: 0.5 },\n\t\t\t\t\tmetalness: { value: 0 },\n\t\t\t\t\tenvMapIntensity : { value: 1 }, // temporary\n\t\t\t\t}\n\n\t\t\t] ),\n\n\t\t\tvertexShader: ShaderChunk.meshphysical_vert,\n\t\t\tfragmentShader: ShaderChunk.meshphysical_frag\n\n\t\t},\n\n\t\tpoints: {\n\n\t\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\t\tUniformsLib.points,\n\t\t\t\tUniformsLib.fog\n\n\t\t\t] ),\n\n\t\t\tvertexShader: ShaderChunk.points_vert,\n\t\t\tfragmentShader: ShaderChunk.points_frag\n\n\t\t},\n\n\t\tdashed: {\n\n\t\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\t\tUniformsLib.common,\n\t\t\t\tUniformsLib.fog,\n\n\t\t\t\t{\n\t\t\t\t\tscale : { value: 1 },\n\t\t\t\t\tdashSize : { value: 1 },\n\t\t\t\t\ttotalSize: { value: 2 }\n\t\t\t\t}\n\n\t\t\t] ),\n\n\t\t\tvertexShader: ShaderChunk.linedashed_vert,\n\t\t\tfragmentShader: ShaderChunk.linedashed_frag\n\n\t\t},\n\n\t\tdepth: {\n\n\t\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\t\tUniformsLib.common,\n\t\t\t\tUniformsLib.displacementmap\n\n\t\t\t] ),\n\n\t\t\tvertexShader: ShaderChunk.depth_vert,\n\t\t\tfragmentShader: ShaderChunk.depth_frag\n\n\t\t},\n\n\t\tnormal: {\n\n\t\t\tuniforms: {\n\n\t\t\t\topacity : { value: 1.0 }\n\n\t\t\t},\n\n\t\t\tvertexShader: ShaderChunk.normal_vert,\n\t\t\tfragmentShader: ShaderChunk.normal_frag\n\n\t\t},\n\n\t\t/* -------------------------------------------------------------------------\n\t\t//\tCube map shader\n\t\t ------------------------------------------------------------------------- */\n\n\t\tcube: {\n\n\t\t\tuniforms: {\n\t\t\t\ttCube: { value: null },\n\t\t\t\ttFlip: { value: - 1 },\n\t\t\t\topacity: { value: 1.0 }\n\t\t\t},\n\n\t\t\tvertexShader: ShaderChunk.cube_vert,\n\t\t\tfragmentShader: ShaderChunk.cube_frag\n\n\t\t},\n\n\t\t/* -------------------------------------------------------------------------\n\t\t//\tCube map shader\n\t\t ------------------------------------------------------------------------- */\n\n\t\tequirect: {\n\n\t\t\tuniforms: {\n\t\t\t\ttEquirect: { value: null },\n\t\t\t\ttFlip: { value: - 1 }\n\t\t\t},\n\n\t\t\tvertexShader: ShaderChunk.equirect_vert,\n\t\t\tfragmentShader: ShaderChunk.equirect_frag\n\n\t\t},\n\n\t\tdistanceRGBA: {\n\n\t\t\tuniforms: {\n\n\t\t\t\tlightPos: { value: new Vector3() }\n\n\t\t\t},\n\n\t\t\tvertexShader: ShaderChunk.distanceRGBA_vert,\n\t\t\tfragmentShader: ShaderChunk.distanceRGBA_frag\n\n\t\t}\n\n\t};\n\n\tShaderLib.physical = {\n\n\t\tuniforms: UniformsUtils.merge( [\n\n\t\t\tShaderLib.standard.uniforms,\n\n\t\t\t{\n\t\t\t\tclearCoat: { value: 0 },\n\t\t\t\tclearCoatRoughness: { value: 0 }\n\t\t\t}\n\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshphysical_vert,\n\t\tfragmentShader: ShaderChunk.meshphysical_frag\n\n\t};\n\n\t/**\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Box2( min, max ) {\n\n\t\tthis.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity );\n\t\tthis.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity );\n\n\t}\n\n\tBox2.prototype = {\n\n\t\tconstructor: Box2,\n\n\t\tset: function ( min, max ) {\n\n\t\t\tthis.min.copy( min );\n\t\t\tthis.max.copy( max );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromPoints: function ( points ) {\n\n\t\t\tthis.makeEmpty();\n\n\t\t\tfor ( var i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\t\tthis.expandByPoint( points[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromCenterAndSize: function () {\n\n\t\t\tvar v1 = new Vector2();\n\n\t\t\treturn function setFromCenterAndSize( center, size ) {\n\n\t\t\t\tvar halfSize = v1.copy( size ).multiplyScalar( 0.5 );\n\t\t\t\tthis.min.copy( center ).sub( halfSize );\n\t\t\t\tthis.max.copy( center ).add( halfSize );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( box ) {\n\n\t\t\tthis.min.copy( box.min );\n\t\t\tthis.max.copy( box.max );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeEmpty: function () {\n\n\t\t\tthis.min.x = this.min.y = + Infinity;\n\t\t\tthis.max.x = this.max.y = - Infinity;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tisEmpty: function () {\n\n\t\t\t// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n\t\t\treturn ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );\n\n\t\t},\n\n\t\tgetCenter: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector2();\n\t\t\treturn this.isEmpty() ? result.set( 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n\t\t},\n\n\t\tgetSize: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector2();\n\t\t\treturn this.isEmpty() ? result.set( 0, 0 ) : result.subVectors( this.max, this.min );\n\n\t\t},\n\n\t\texpandByPoint: function ( point ) {\n\n\t\t\tthis.min.min( point );\n\t\t\tthis.max.max( point );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\texpandByVector: function ( vector ) {\n\n\t\t\tthis.min.sub( vector );\n\t\t\tthis.max.add( vector );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\texpandByScalar: function ( scalar ) {\n\n\t\t\tthis.min.addScalar( - scalar );\n\t\t\tthis.max.addScalar( scalar );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcontainsPoint: function ( point ) {\n\n\t\t\tif ( point.x < this.min.x || point.x > this.max.x ||\n\t\t\t point.y < this.min.y || point.y > this.max.y ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t},\n\n\t\tcontainsBox: function ( box ) {\n\n\t\t\tif ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&\n\t\t\t ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {\n\n\t\t\t\treturn true;\n\n\t\t\t}\n\n\t\t\treturn false;\n\n\t\t},\n\n\t\tgetParameter: function ( point, optionalTarget ) {\n\n\t\t\t// This can potentially have a divide by zero if the box\n\t\t\t// has a size dimension of 0.\n\n\t\t\tvar result = optionalTarget || new Vector2();\n\n\t\t\treturn result.set(\n\t\t\t\t( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n\t\t\t\t( point.y - this.min.y ) / ( this.max.y - this.min.y )\n\t\t\t);\n\n\t\t},\n\n\t\tintersectsBox: function ( box ) {\n\n\t\t\t// using 6 splitting planes to rule out intersections.\n\n\t\t\tif ( box.max.x < this.min.x || box.min.x > this.max.x ||\n\t\t\t box.max.y < this.min.y || box.min.y > this.max.y ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t},\n\n\t\tclampPoint: function ( point, optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector2();\n\t\t\treturn result.copy( point ).clamp( this.min, this.max );\n\n\t\t},\n\n\t\tdistanceToPoint: function () {\n\n\t\t\tvar v1 = new Vector2();\n\n\t\t\treturn function distanceToPoint( point ) {\n\n\t\t\t\tvar clampedPoint = v1.copy( point ).clamp( this.min, this.max );\n\t\t\t\treturn clampedPoint.sub( point ).length();\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tintersect: function ( box ) {\n\n\t\t\tthis.min.max( box.min );\n\t\t\tthis.max.min( box.max );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tunion: function ( box ) {\n\n\t\t\tthis.min.min( box.min );\n\t\t\tthis.max.max( box.max );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttranslate: function ( offset ) {\n\n\t\t\tthis.min.add( offset );\n\t\t\tthis.max.add( offset );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( box ) {\n\n\t\t\treturn box.min.equals( this.min ) && box.max.equals( this.max );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction LensFlarePlugin( renderer, flares ) {\n\n\t\tvar gl = renderer.context;\n\t\tvar state = renderer.state;\n\n\t\tvar vertexBuffer, elementBuffer;\n\t\tvar shader, program, attributes, uniforms;\n\n\t\tvar tempTexture, occlusionTexture;\n\n\t\tfunction init() {\n\n\t\t\tvar vertices = new Float32Array( [\n\t\t\t\t- 1, - 1, 0, 0,\n\t\t\t\t 1, - 1, 1, 0,\n\t\t\t\t 1, 1, 1, 1,\n\t\t\t\t- 1, 1, 0, 1\n\t\t\t] );\n\n\t\t\tvar faces = new Uint16Array( [\n\t\t\t\t0, 1, 2,\n\t\t\t\t0, 2, 3\n\t\t\t] );\n\n\t\t\t// buffers\n\n\t\t\tvertexBuffer = gl.createBuffer();\n\t\t\telementBuffer = gl.createBuffer();\n\n\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n\t\t\tgl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );\n\n\t\t\tgl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n\t\t\tgl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );\n\n\t\t\t// textures\n\n\t\t\ttempTexture = gl.createTexture();\n\t\t\tocclusionTexture = gl.createTexture();\n\n\t\t\tstate.bindTexture( gl.TEXTURE_2D, tempTexture );\n\t\t\tgl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\n\n\t\t\tstate.bindTexture( gl.TEXTURE_2D, occlusionTexture );\n\t\t\tgl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\n\t\t\tgl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\n\n\t\t\tshader = {\n\n\t\t\t\tvertexShader: [\n\n\t\t\t\t\t\"uniform lowp int renderType;\",\n\n\t\t\t\t\t\"uniform vec3 screenPosition;\",\n\t\t\t\t\t\"uniform vec2 scale;\",\n\t\t\t\t\t\"uniform float rotation;\",\n\n\t\t\t\t\t\"uniform sampler2D occlusionMap;\",\n\n\t\t\t\t\t\"attribute vec2 position;\",\n\t\t\t\t\t\"attribute vec2 uv;\",\n\n\t\t\t\t\t\"varying vec2 vUV;\",\n\t\t\t\t\t\"varying float vVisibility;\",\n\n\t\t\t\t\t\"void main() {\",\n\n\t\t\t\t\t\t\"vUV = uv;\",\n\n\t\t\t\t\t\t\"vec2 pos = position;\",\n\n\t\t\t\t\t\t\"if ( renderType == 2 ) {\",\n\n\t\t\t\t\t\t\t\"vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );\",\n\t\t\t\t\t\t\t\"visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );\",\n\n\t\t\t\t\t\t\t\"vVisibility = visibility.r / 9.0;\",\n\t\t\t\t\t\t\t\"vVisibility *= 1.0 - visibility.g / 9.0;\",\n\t\t\t\t\t\t\t\"vVisibility *= visibility.b / 9.0;\",\n\t\t\t\t\t\t\t\"vVisibility *= 1.0 - visibility.a / 9.0;\",\n\n\t\t\t\t\t\t\t\"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\",\n\t\t\t\t\t\t\t\"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\",\n\n\t\t\t\t\t\t\"}\",\n\n\t\t\t\t\t\t\"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\",\n\n\t\t\t\t\t\"}\"\n\n\t\t\t\t].join( \"\\n\" ),\n\n\t\t\t\tfragmentShader: [\n\n\t\t\t\t\t\"uniform lowp int renderType;\",\n\n\t\t\t\t\t\"uniform sampler2D map;\",\n\t\t\t\t\t\"uniform float opacity;\",\n\t\t\t\t\t\"uniform vec3 color;\",\n\n\t\t\t\t\t\"varying vec2 vUV;\",\n\t\t\t\t\t\"varying float vVisibility;\",\n\n\t\t\t\t\t\"void main() {\",\n\n\t\t\t\t\t\t// pink square\n\n\t\t\t\t\t\t\"if ( renderType == 0 ) {\",\n\n\t\t\t\t\t\t\t\"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );\",\n\n\t\t\t\t\t\t// restore\n\n\t\t\t\t\t\t\"} else if ( renderType == 1 ) {\",\n\n\t\t\t\t\t\t\t\"gl_FragColor = texture2D( map, vUV );\",\n\n\t\t\t\t\t\t// flare\n\n\t\t\t\t\t\t\"} else {\",\n\n\t\t\t\t\t\t\t\"vec4 texture = texture2D( map, vUV );\",\n\t\t\t\t\t\t\t\"texture.a *= opacity * vVisibility;\",\n\t\t\t\t\t\t\t\"gl_FragColor = texture;\",\n\t\t\t\t\t\t\t\"gl_FragColor.rgb *= color;\",\n\n\t\t\t\t\t\t\"}\",\n\n\t\t\t\t\t\"}\"\n\n\t\t\t\t].join( \"\\n\" )\n\n\t\t\t};\n\n\t\t\tprogram = createProgram( shader );\n\n\t\t\tattributes = {\n\t\t\t\tvertex: gl.getAttribLocation ( program, \"position\" ),\n\t\t\t\tuv: gl.getAttribLocation ( program, \"uv\" )\n\t\t\t};\n\n\t\t\tuniforms = {\n\t\t\t\trenderType: gl.getUniformLocation( program, \"renderType\" ),\n\t\t\t\tmap: gl.getUniformLocation( program, \"map\" ),\n\t\t\t\tocclusionMap: gl.getUniformLocation( program, \"occlusionMap\" ),\n\t\t\t\topacity: gl.getUniformLocation( program, \"opacity\" ),\n\t\t\t\tcolor: gl.getUniformLocation( program, \"color\" ),\n\t\t\t\tscale: gl.getUniformLocation( program, \"scale\" ),\n\t\t\t\trotation: gl.getUniformLocation( program, \"rotation\" ),\n\t\t\t\tscreenPosition: gl.getUniformLocation( program, \"screenPosition\" )\n\t\t\t};\n\n\t\t}\n\n\t\t/*\n\t\t * Render lens flares\n\t\t * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,\n\t\t * reads these back and calculates occlusion.\n\t\t */\n\n\t\tthis.render = function ( scene, camera, viewport ) {\n\n\t\t\tif ( flares.length === 0 ) return;\n\n\t\t\tvar tempPosition = new Vector3();\n\n\t\t\tvar invAspect = viewport.w / viewport.z,\n\t\t\t\thalfViewportWidth = viewport.z * 0.5,\n\t\t\t\thalfViewportHeight = viewport.w * 0.5;\n\n\t\t\tvar size = 16 / viewport.w,\n\t\t\t\tscale = new Vector2( size * invAspect, size );\n\n\t\t\tvar screenPosition = new Vector3( 1, 1, 0 ),\n\t\t\t\tscreenPositionPixels = new Vector2( 1, 1 );\n\n\t\t\tvar validArea = new Box2();\n\n\t\t\tvalidArea.min.set( viewport.x, viewport.y );\n\t\t\tvalidArea.max.set( viewport.x + ( viewport.z - 16 ), viewport.y + ( viewport.w - 16 ) );\n\n\t\t\tif ( program === undefined ) {\n\n\t\t\t\tinit();\n\n\t\t\t}\n\n\t\t\tgl.useProgram( program );\n\n\t\t\tstate.initAttributes();\n\t\t\tstate.enableAttribute( attributes.vertex );\n\t\t\tstate.enableAttribute( attributes.uv );\n\t\t\tstate.disableUnusedAttributes();\n\n\t\t\t// loop through all lens flares to update their occlusion and positions\n\t\t\t// setup gl and common used attribs/uniforms\n\n\t\t\tgl.uniform1i( uniforms.occlusionMap, 0 );\n\t\t\tgl.uniform1i( uniforms.map, 1 );\n\n\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n\t\t\tgl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );\n\t\t\tgl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );\n\n\t\t\tgl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n\n\t\t\tstate.disable( gl.CULL_FACE );\n\t\t\tstate.setDepthWrite( false );\n\n\t\t\tfor ( var i = 0, l = flares.length; i < l; i ++ ) {\n\n\t\t\t\tsize = 16 / viewport.w;\n\t\t\t\tscale.set( size * invAspect, size );\n\n\t\t\t\t// calc object screen position\n\n\t\t\t\tvar flare = flares[ i ];\n\n\t\t\t\ttempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] );\n\n\t\t\t\ttempPosition.applyMatrix4( camera.matrixWorldInverse );\n\t\t\t\ttempPosition.applyProjection( camera.projectionMatrix );\n\n\t\t\t\t// setup arrays for gl programs\n\n\t\t\t\tscreenPosition.copy( tempPosition );\n\n\t\t\t\t// horizontal and vertical coordinate of the lower left corner of the pixels to copy\n\n\t\t\t\tscreenPositionPixels.x = viewport.x + ( screenPosition.x * halfViewportWidth ) + halfViewportWidth - 8;\n\t\t\t\tscreenPositionPixels.y = viewport.y + ( screenPosition.y * halfViewportHeight ) + halfViewportHeight - 8;\n\n\t\t\t\t// screen cull\n\n\t\t\t\tif ( validArea.containsPoint( screenPositionPixels ) === true ) {\n\n\t\t\t\t\t// save current RGB to temp texture\n\n\t\t\t\t\tstate.activeTexture( gl.TEXTURE0 );\n\t\t\t\t\tstate.bindTexture( gl.TEXTURE_2D, null );\n\t\t\t\t\tstate.activeTexture( gl.TEXTURE1 );\n\t\t\t\t\tstate.bindTexture( gl.TEXTURE_2D, tempTexture );\n\t\t\t\t\tgl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 );\n\n\n\t\t\t\t\t// render pink quad\n\n\t\t\t\t\tgl.uniform1i( uniforms.renderType, 0 );\n\t\t\t\t\tgl.uniform2f( uniforms.scale, scale.x, scale.y );\n\t\t\t\t\tgl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );\n\n\t\t\t\t\tstate.disable( gl.BLEND );\n\t\t\t\t\tstate.enable( gl.DEPTH_TEST );\n\n\t\t\t\t\tgl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );\n\n\n\t\t\t\t\t// copy result to occlusionMap\n\n\t\t\t\t\tstate.activeTexture( gl.TEXTURE0 );\n\t\t\t\t\tstate.bindTexture( gl.TEXTURE_2D, occlusionTexture );\n\t\t\t\t\tgl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 );\n\n\n\t\t\t\t\t// restore graphics\n\n\t\t\t\t\tgl.uniform1i( uniforms.renderType, 1 );\n\t\t\t\t\tstate.disable( gl.DEPTH_TEST );\n\n\t\t\t\t\tstate.activeTexture( gl.TEXTURE1 );\n\t\t\t\t\tstate.bindTexture( gl.TEXTURE_2D, tempTexture );\n\t\t\t\t\tgl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );\n\n\n\t\t\t\t\t// update object positions\n\n\t\t\t\t\tflare.positionScreen.copy( screenPosition );\n\n\t\t\t\t\tif ( flare.customUpdateCallback ) {\n\n\t\t\t\t\t\tflare.customUpdateCallback( flare );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tflare.updateLensFlares();\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// render flares\n\n\t\t\t\t\tgl.uniform1i( uniforms.renderType, 2 );\n\t\t\t\t\tstate.enable( gl.BLEND );\n\n\t\t\t\t\tfor ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {\n\n\t\t\t\t\t\tvar sprite = flare.lensFlares[ j ];\n\n\t\t\t\t\t\tif ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {\n\n\t\t\t\t\t\t\tscreenPosition.x = sprite.x;\n\t\t\t\t\t\t\tscreenPosition.y = sprite.y;\n\t\t\t\t\t\t\tscreenPosition.z = sprite.z;\n\n\t\t\t\t\t\t\tsize = sprite.size * sprite.scale / viewport.w;\n\n\t\t\t\t\t\t\tscale.x = size * invAspect;\n\t\t\t\t\t\t\tscale.y = size;\n\n\t\t\t\t\t\t\tgl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );\n\t\t\t\t\t\t\tgl.uniform2f( uniforms.scale, scale.x, scale.y );\n\t\t\t\t\t\t\tgl.uniform1f( uniforms.rotation, sprite.rotation );\n\n\t\t\t\t\t\t\tgl.uniform1f( uniforms.opacity, sprite.opacity );\n\t\t\t\t\t\t\tgl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );\n\n\t\t\t\t\t\t\tstate.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );\n\t\t\t\t\t\t\trenderer.setTexture2D( sprite.texture, 1 );\n\n\t\t\t\t\t\t\tgl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// restore gl\n\n\t\t\tstate.enable( gl.CULL_FACE );\n\t\t\tstate.enable( gl.DEPTH_TEST );\n\t\t\tstate.setDepthWrite( true );\n\n\t\t\trenderer.resetGLState();\n\n\t\t};\n\n\t\tfunction createProgram( shader ) {\n\n\t\t\tvar program = gl.createProgram();\n\n\t\t\tvar fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );\n\t\t\tvar vertexShader = gl.createShader( gl.VERTEX_SHADER );\n\n\t\t\tvar prefix = \"precision \" + renderer.getPrecision() + \" float;\\n\";\n\n\t\t\tgl.shaderSource( fragmentShader, prefix + shader.fragmentShader );\n\t\t\tgl.shaderSource( vertexShader, prefix + shader.vertexShader );\n\n\t\t\tgl.compileShader( fragmentShader );\n\t\t\tgl.compileShader( vertexShader );\n\n\t\t\tgl.attachShader( program, fragmentShader );\n\t\t\tgl.attachShader( program, vertexShader );\n\n\t\t\tgl.linkProgram( program );\n\n\t\t\treturn program;\n\n\t\t}\n\n\t}\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction SpritePlugin( renderer, sprites ) {\n\n\t\tvar gl = renderer.context;\n\t\tvar state = renderer.state;\n\n\t\tvar vertexBuffer, elementBuffer;\n\t\tvar program, attributes, uniforms;\n\n\t\tvar texture;\n\n\t\t// decompose matrixWorld\n\n\t\tvar spritePosition = new Vector3();\n\t\tvar spriteRotation = new Quaternion();\n\t\tvar spriteScale = new Vector3();\n\n\t\tfunction init() {\n\n\t\t\tvar vertices = new Float32Array( [\n\t\t\t\t- 0.5, - 0.5, 0, 0,\n\t\t\t\t 0.5, - 0.5, 1, 0,\n\t\t\t\t 0.5, 0.5, 1, 1,\n\t\t\t\t- 0.5, 0.5, 0, 1\n\t\t\t] );\n\n\t\t\tvar faces = new Uint16Array( [\n\t\t\t\t0, 1, 2,\n\t\t\t\t0, 2, 3\n\t\t\t] );\n\n\t\t\tvertexBuffer = gl.createBuffer();\n\t\t\telementBuffer = gl.createBuffer();\n\n\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n\t\t\tgl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );\n\n\t\t\tgl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n\t\t\tgl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );\n\n\t\t\tprogram = createProgram();\n\n\t\t\tattributes = {\n\t\t\t\tposition:\t\t\tgl.getAttribLocation ( program, 'position' ),\n\t\t\t\tuv:\t\t\t\t\tgl.getAttribLocation ( program, 'uv' )\n\t\t\t};\n\n\t\t\tuniforms = {\n\t\t\t\tuvOffset:\t\t\tgl.getUniformLocation( program, 'uvOffset' ),\n\t\t\t\tuvScale:\t\t\tgl.getUniformLocation( program, 'uvScale' ),\n\n\t\t\t\trotation:\t\t\tgl.getUniformLocation( program, 'rotation' ),\n\t\t\t\tscale:\t\t\t\tgl.getUniformLocation( program, 'scale' ),\n\n\t\t\t\tcolor:\t\t\t\tgl.getUniformLocation( program, 'color' ),\n\t\t\t\tmap:\t\t\t\tgl.getUniformLocation( program, 'map' ),\n\t\t\t\topacity:\t\t\tgl.getUniformLocation( program, 'opacity' ),\n\n\t\t\t\tmodelViewMatrix: \tgl.getUniformLocation( program, 'modelViewMatrix' ),\n\t\t\t\tprojectionMatrix:\tgl.getUniformLocation( program, 'projectionMatrix' ),\n\n\t\t\t\tfogType:\t\t\tgl.getUniformLocation( program, 'fogType' ),\n\t\t\t\tfogDensity:\t\t\tgl.getUniformLocation( program, 'fogDensity' ),\n\t\t\t\tfogNear:\t\t\tgl.getUniformLocation( program, 'fogNear' ),\n\t\t\t\tfogFar:\t\t\t\tgl.getUniformLocation( program, 'fogFar' ),\n\t\t\t\tfogColor:\t\t\tgl.getUniformLocation( program, 'fogColor' ),\n\n\t\t\t\talphaTest:\t\t\tgl.getUniformLocation( program, 'alphaTest' )\n\t\t\t};\n\n\t\t\tvar canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n\t\t\tcanvas.width = 8;\n\t\t\tcanvas.height = 8;\n\n\t\t\tvar context = canvas.getContext( '2d' );\n\t\t\tcontext.fillStyle = 'white';\n\t\t\tcontext.fillRect( 0, 0, 8, 8 );\n\n\t\t\ttexture = new Texture( canvas );\n\t\t\ttexture.needsUpdate = true;\n\n\t\t}\n\n\t\tthis.render = function ( scene, camera ) {\n\n\t\t\tif ( sprites.length === 0 ) return;\n\n\t\t\t// setup gl\n\n\t\t\tif ( program === undefined ) {\n\n\t\t\t\tinit();\n\n\t\t\t}\n\n\t\t\tgl.useProgram( program );\n\n\t\t\tstate.initAttributes();\n\t\t\tstate.enableAttribute( attributes.position );\n\t\t\tstate.enableAttribute( attributes.uv );\n\t\t\tstate.disableUnusedAttributes();\n\n\t\t\tstate.disable( gl.CULL_FACE );\n\t\t\tstate.enable( gl.BLEND );\n\n\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n\t\t\tgl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );\n\t\t\tgl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );\n\n\t\t\tgl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n\n\t\t\tgl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );\n\n\t\t\tstate.activeTexture( gl.TEXTURE0 );\n\t\t\tgl.uniform1i( uniforms.map, 0 );\n\n\t\t\tvar oldFogType = 0;\n\t\t\tvar sceneFogType = 0;\n\t\t\tvar fog = scene.fog;\n\n\t\t\tif ( fog ) {\n\n\t\t\t\tgl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );\n\n\t\t\t\tif ( (fog && fog.isFog) ) {\n\n\t\t\t\t\tgl.uniform1f( uniforms.fogNear, fog.near );\n\t\t\t\t\tgl.uniform1f( uniforms.fogFar, fog.far );\n\n\t\t\t\t\tgl.uniform1i( uniforms.fogType, 1 );\n\t\t\t\t\toldFogType = 1;\n\t\t\t\t\tsceneFogType = 1;\n\n\t\t\t\t} else if ( (fog && fog.isFogExp2) ) {\n\n\t\t\t\t\tgl.uniform1f( uniforms.fogDensity, fog.density );\n\n\t\t\t\t\tgl.uniform1i( uniforms.fogType, 2 );\n\t\t\t\t\toldFogType = 2;\n\t\t\t\t\tsceneFogType = 2;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tgl.uniform1i( uniforms.fogType, 0 );\n\t\t\t\toldFogType = 0;\n\t\t\t\tsceneFogType = 0;\n\n\t\t\t}\n\n\n\t\t\t// update positions and sort\n\n\t\t\tfor ( var i = 0, l = sprites.length; i < l; i ++ ) {\n\n\t\t\t\tvar sprite = sprites[ i ];\n\n\t\t\t\tsprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );\n\t\t\t\tsprite.z = - sprite.modelViewMatrix.elements[ 14 ];\n\n\t\t\t}\n\n\t\t\tsprites.sort( painterSortStable );\n\n\t\t\t// render all sprites\n\n\t\t\tvar scale = [];\n\n\t\t\tfor ( var i = 0, l = sprites.length; i < l; i ++ ) {\n\n\t\t\t\tvar sprite = sprites[ i ];\n\t\t\t\tvar material = sprite.material;\n\n\t\t\t\tif ( material.visible === false ) continue;\n\n\t\t\t\tgl.uniform1f( uniforms.alphaTest, material.alphaTest );\n\t\t\t\tgl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements );\n\n\t\t\t\tsprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );\n\n\t\t\t\tscale[ 0 ] = spriteScale.x;\n\t\t\t\tscale[ 1 ] = spriteScale.y;\n\n\t\t\t\tvar fogType = 0;\n\n\t\t\t\tif ( scene.fog && material.fog ) {\n\n\t\t\t\t\tfogType = sceneFogType;\n\n\t\t\t\t}\n\n\t\t\t\tif ( oldFogType !== fogType ) {\n\n\t\t\t\t\tgl.uniform1i( uniforms.fogType, fogType );\n\t\t\t\t\toldFogType = fogType;\n\n\t\t\t\t}\n\n\t\t\t\tif ( material.map !== null ) {\n\n\t\t\t\t\tgl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );\n\t\t\t\t\tgl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tgl.uniform2f( uniforms.uvOffset, 0, 0 );\n\t\t\t\t\tgl.uniform2f( uniforms.uvScale, 1, 1 );\n\n\t\t\t\t}\n\n\t\t\t\tgl.uniform1f( uniforms.opacity, material.opacity );\n\t\t\t\tgl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );\n\n\t\t\t\tgl.uniform1f( uniforms.rotation, material.rotation );\n\t\t\t\tgl.uniform2fv( uniforms.scale, scale );\n\n\t\t\t\tstate.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );\n\t\t\t\tstate.setDepthTest( material.depthTest );\n\t\t\t\tstate.setDepthWrite( material.depthWrite );\n\n\t\t\t\tif ( material.map ) {\n\n\t\t\t\t\trenderer.setTexture2D( material.map, 0 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderer.setTexture2D( texture, 0 );\n\n\t\t\t\t}\n\n\t\t\t\tgl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );\n\n\t\t\t}\n\n\t\t\t// restore gl\n\n\t\t\tstate.enable( gl.CULL_FACE );\n\n\t\t\trenderer.resetGLState();\n\n\t\t};\n\n\t\tfunction createProgram() {\n\n\t\t\tvar program = gl.createProgram();\n\n\t\t\tvar vertexShader = gl.createShader( gl.VERTEX_SHADER );\n\t\t\tvar fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );\n\n\t\t\tgl.shaderSource( vertexShader, [\n\n\t\t\t\t'precision ' + renderer.getPrecision() + ' float;',\n\n\t\t\t\t'uniform mat4 modelViewMatrix;',\n\t\t\t\t'uniform mat4 projectionMatrix;',\n\t\t\t\t'uniform float rotation;',\n\t\t\t\t'uniform vec2 scale;',\n\t\t\t\t'uniform vec2 uvOffset;',\n\t\t\t\t'uniform vec2 uvScale;',\n\n\t\t\t\t'attribute vec2 position;',\n\t\t\t\t'attribute vec2 uv;',\n\n\t\t\t\t'varying vec2 vUV;',\n\n\t\t\t\t'void main() {',\n\n\t\t\t\t\t'vUV = uvOffset + uv * uvScale;',\n\n\t\t\t\t\t'vec2 alignedPosition = position * scale;',\n\n\t\t\t\t\t'vec2 rotatedPosition;',\n\t\t\t\t\t'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',\n\t\t\t\t\t'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',\n\n\t\t\t\t\t'vec4 finalPosition;',\n\n\t\t\t\t\t'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',\n\t\t\t\t\t'finalPosition.xy += rotatedPosition;',\n\t\t\t\t\t'finalPosition = projectionMatrix * finalPosition;',\n\n\t\t\t\t\t'gl_Position = finalPosition;',\n\n\t\t\t\t'}'\n\n\t\t\t].join( '\\n' ) );\n\n\t\t\tgl.shaderSource( fragmentShader, [\n\n\t\t\t\t'precision ' + renderer.getPrecision() + ' float;',\n\n\t\t\t\t'uniform vec3 color;',\n\t\t\t\t'uniform sampler2D map;',\n\t\t\t\t'uniform float opacity;',\n\n\t\t\t\t'uniform int fogType;',\n\t\t\t\t'uniform vec3 fogColor;',\n\t\t\t\t'uniform float fogDensity;',\n\t\t\t\t'uniform float fogNear;',\n\t\t\t\t'uniform float fogFar;',\n\t\t\t\t'uniform float alphaTest;',\n\n\t\t\t\t'varying vec2 vUV;',\n\n\t\t\t\t'void main() {',\n\n\t\t\t\t\t'vec4 texture = texture2D( map, vUV );',\n\n\t\t\t\t\t'if ( texture.a < alphaTest ) discard;',\n\n\t\t\t\t\t'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',\n\n\t\t\t\t\t'if ( fogType > 0 ) {',\n\n\t\t\t\t\t\t'float depth = gl_FragCoord.z / gl_FragCoord.w;',\n\t\t\t\t\t\t'float fogFactor = 0.0;',\n\n\t\t\t\t\t\t'if ( fogType == 1 ) {',\n\n\t\t\t\t\t\t\t'fogFactor = smoothstep( fogNear, fogFar, depth );',\n\n\t\t\t\t\t\t'} else {',\n\n\t\t\t\t\t\t\t'const float LOG2 = 1.442695;',\n\t\t\t\t\t\t\t'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',\n\t\t\t\t\t\t\t'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',\n\n\t\t\t\t\t\t'}',\n\n\t\t\t\t\t\t'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',\n\n\t\t\t\t\t'}',\n\n\t\t\t\t'}'\n\n\t\t\t].join( '\\n' ) );\n\n\t\t\tgl.compileShader( vertexShader );\n\t\t\tgl.compileShader( fragmentShader );\n\n\t\t\tgl.attachShader( program, vertexShader );\n\t\t\tgl.attachShader( program, fragmentShader );\n\n\t\t\tgl.linkProgram( program );\n\n\t\t\treturn program;\n\n\t\t}\n\n\t\tfunction painterSortStable( a, b ) {\n\n\t\t\tif ( a.renderOrder !== b.renderOrder ) {\n\n\t\t\t\treturn a.renderOrder - b.renderOrder;\n\n\t\t\t} else if ( a.z !== b.z ) {\n\n\t\t\t\treturn b.z - a.z;\n\n\t\t\t} else {\n\n\t\t\t\treturn b.id - a.id;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction Material() {\n\n\t\tObject.defineProperty( this, 'id', { value: MaterialIdCount() } );\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'Material';\n\n\t\tthis.fog = true;\n\t\tthis.lights = true;\n\n\t\tthis.blending = NormalBlending;\n\t\tthis.side = FrontSide;\n\t\tthis.shading = SmoothShading; // THREE.FlatShading, THREE.SmoothShading\n\t\tthis.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors\n\n\t\tthis.opacity = 1;\n\t\tthis.transparent = false;\n\n\t\tthis.blendSrc = SrcAlphaFactor;\n\t\tthis.blendDst = OneMinusSrcAlphaFactor;\n\t\tthis.blendEquation = AddEquation;\n\t\tthis.blendSrcAlpha = null;\n\t\tthis.blendDstAlpha = null;\n\t\tthis.blendEquationAlpha = null;\n\n\t\tthis.depthFunc = LessEqualDepth;\n\t\tthis.depthTest = true;\n\t\tthis.depthWrite = true;\n\n\t\tthis.clippingPlanes = null;\n\t\tthis.clipIntersection = false;\n\t\tthis.clipShadows = false;\n\n\t\tthis.colorWrite = true;\n\n\t\tthis.precision = null; // override the renderer's default precision for this material\n\n\t\tthis.polygonOffset = false;\n\t\tthis.polygonOffsetFactor = 0;\n\t\tthis.polygonOffsetUnits = 0;\n\n\t\tthis.alphaTest = 0;\n\t\tthis.premultipliedAlpha = false;\n\n\t\tthis.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer\n\n\t\tthis.visible = true;\n\n\t\tthis._needsUpdate = true;\n\n\t}\n\n\tMaterial.prototype = {\n\n\t\tconstructor: Material,\n\n\t\tisMaterial: true,\n\n\t\tget needsUpdate() {\n\n\t\t\treturn this._needsUpdate;\n\n\t\t},\n\n\t\tset needsUpdate( value ) {\n\n\t\t\tif ( value === true ) this.update();\n\t\t\tthis._needsUpdate = value;\n\n\t\t},\n\n\t\tsetValues: function ( values ) {\n\n\t\t\tif ( values === undefined ) return;\n\n\t\t\tfor ( var key in values ) {\n\n\t\t\t\tvar newValue = values[ key ];\n\n\t\t\t\tif ( newValue === undefined ) {\n\n\t\t\t\t\tconsole.warn( \"THREE.Material: '\" + key + \"' parameter is undefined.\" );\n\t\t\t\t\tcontinue;\n\n\t\t\t\t}\n\n\t\t\t\tvar currentValue = this[ key ];\n\n\t\t\t\tif ( currentValue === undefined ) {\n\n\t\t\t\t\tconsole.warn( \"THREE.\" + this.type + \": '\" + key + \"' is not a property of this material.\" );\n\t\t\t\t\tcontinue;\n\n\t\t\t\t}\n\n\t\t\t\tif ( (currentValue && currentValue.isColor) ) {\n\n\t\t\t\t\tcurrentValue.set( newValue );\n\n\t\t\t\t} else if ( (currentValue && currentValue.isVector3) && (newValue && newValue.isVector3) ) {\n\n\t\t\t\t\tcurrentValue.copy( newValue );\n\n\t\t\t\t} else if ( key === 'overdraw' ) {\n\n\t\t\t\t\t// ensure overdraw is backwards-compatible with legacy boolean type\n\t\t\t\t\tthis[ key ] = Number( newValue );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis[ key ] = newValue;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t},\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\tvar isRoot = meta === undefined;\n\n\t\t\tif ( isRoot ) {\n\n\t\t\t\tmeta = {\n\t\t\t\t\ttextures: {},\n\t\t\t\t\timages: {}\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\tvar data = {\n\t\t\t\tmetadata: {\n\t\t\t\t\tversion: 4.4,\n\t\t\t\t\ttype: 'Material',\n\t\t\t\t\tgenerator: 'Material.toJSON'\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// standard Material serialization\n\t\t\tdata.uuid = this.uuid;\n\t\t\tdata.type = this.type;\n\n\t\t\tif ( this.name !== '' ) data.name = this.name;\n\n\t\t\tif ( (this.color && this.color.isColor) ) data.color = this.color.getHex();\n\n\t\t\tif ( this.roughness !== undefined ) data.roughness = this.roughness;\n\t\t\tif ( this.metalness !== undefined ) data.metalness = this.metalness;\n\n\t\t\tif ( (this.emissive && this.emissive.isColor) ) data.emissive = this.emissive.getHex();\n\t\t\tif ( (this.specular && this.specular.isColor) ) data.specular = this.specular.getHex();\n\t\t\tif ( this.shininess !== undefined ) data.shininess = this.shininess;\n\n\t\t\tif ( (this.map && this.map.isTexture) ) data.map = this.map.toJSON( meta ).uuid;\n\t\t\tif ( (this.alphaMap && this.alphaMap.isTexture) ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;\n\t\t\tif ( (this.lightMap && this.lightMap.isTexture) ) data.lightMap = this.lightMap.toJSON( meta ).uuid;\n\t\t\tif ( (this.bumpMap && this.bumpMap.isTexture) ) {\n\n\t\t\t\tdata.bumpMap = this.bumpMap.toJSON( meta ).uuid;\n\t\t\t\tdata.bumpScale = this.bumpScale;\n\n\t\t\t}\n\t\t\tif ( (this.normalMap && this.normalMap.isTexture) ) {\n\n\t\t\t\tdata.normalMap = this.normalMap.toJSON( meta ).uuid;\n\t\t\t\tdata.normalScale = this.normalScale.toArray();\n\n\t\t\t}\n\t\t\tif ( (this.displacementMap && this.displacementMap.isTexture) ) {\n\n\t\t\t\tdata.displacementMap = this.displacementMap.toJSON( meta ).uuid;\n\t\t\t\tdata.displacementScale = this.displacementScale;\n\t\t\t\tdata.displacementBias = this.displacementBias;\n\n\t\t\t}\n\t\t\tif ( (this.roughnessMap && this.roughnessMap.isTexture) ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;\n\t\t\tif ( (this.metalnessMap && this.metalnessMap.isTexture) ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;\n\n\t\t\tif ( (this.emissiveMap && this.emissiveMap.isTexture) ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;\n\t\t\tif ( (this.specularMap && this.specularMap.isTexture) ) data.specularMap = this.specularMap.toJSON( meta ).uuid;\n\n\t\t\tif ( (this.envMap && this.envMap.isTexture) ) {\n\n\t\t\t\tdata.envMap = this.envMap.toJSON( meta ).uuid;\n\t\t\t\tdata.reflectivity = this.reflectivity; // Scale behind envMap\n\n\t\t\t}\n\n\t\t\tif ( this.size !== undefined ) data.size = this.size;\n\t\t\tif ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;\n\n\t\t\tif ( this.blending !== NormalBlending ) data.blending = this.blending;\n\t\t\tif ( this.shading !== SmoothShading ) data.shading = this.shading;\n\t\t\tif ( this.side !== FrontSide ) data.side = this.side;\n\t\t\tif ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors;\n\n\t\t\tif ( this.opacity < 1 ) data.opacity = this.opacity;\n\t\t\tif ( this.transparent === true ) data.transparent = this.transparent;\n\n\t\t\tdata.depthFunc = this.depthFunc;\n\t\t\tdata.depthTest = this.depthTest;\n\t\t\tdata.depthWrite = this.depthWrite;\n\n\t\t\tif ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;\n\t\t\tif ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;\n\t\t\tif ( this.wireframe === true ) data.wireframe = this.wireframe;\n\t\t\tif ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;\n\t\t\tif ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;\n\t\t\tif ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;\n\n\t\t\tdata.skinning = this.skinning;\n\t\t\tdata.morphTargets = this.morphTargets;\n\n\t\t\t// TODO: Copied from Object3D.toJSON\n\n\t\t\tfunction extractFromCache( cache ) {\n\n\t\t\t\tvar values = [];\n\n\t\t\t\tfor ( var key in cache ) {\n\n\t\t\t\t\tvar data = cache[ key ];\n\t\t\t\t\tdelete data.metadata;\n\t\t\t\t\tvalues.push( data );\n\n\t\t\t\t}\n\n\t\t\t\treturn values;\n\n\t\t\t}\n\n\t\t\tif ( isRoot ) {\n\n\t\t\t\tvar textures = extractFromCache( meta.textures );\n\t\t\t\tvar images = extractFromCache( meta.images );\n\n\t\t\t\tif ( textures.length > 0 ) data.textures = textures;\n\t\t\t\tif ( images.length > 0 ) data.images = images;\n\n\t\t\t}\n\n\t\t\treturn data;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tthis.name = source.name;\n\n\t\t\tthis.fog = source.fog;\n\t\t\tthis.lights = source.lights;\n\n\t\t\tthis.blending = source.blending;\n\t\t\tthis.side = source.side;\n\t\t\tthis.shading = source.shading;\n\t\t\tthis.vertexColors = source.vertexColors;\n\n\t\t\tthis.opacity = source.opacity;\n\t\t\tthis.transparent = source.transparent;\n\n\t\t\tthis.blendSrc = source.blendSrc;\n\t\t\tthis.blendDst = source.blendDst;\n\t\t\tthis.blendEquation = source.blendEquation;\n\t\t\tthis.blendSrcAlpha = source.blendSrcAlpha;\n\t\t\tthis.blendDstAlpha = source.blendDstAlpha;\n\t\t\tthis.blendEquationAlpha = source.blendEquationAlpha;\n\n\t\t\tthis.depthFunc = source.depthFunc;\n\t\t\tthis.depthTest = source.depthTest;\n\t\t\tthis.depthWrite = source.depthWrite;\n\n\t\t\tthis.colorWrite = source.colorWrite;\n\n\t\t\tthis.precision = source.precision;\n\n\t\t\tthis.polygonOffset = source.polygonOffset;\n\t\t\tthis.polygonOffsetFactor = source.polygonOffsetFactor;\n\t\t\tthis.polygonOffsetUnits = source.polygonOffsetUnits;\n\n\t\t\tthis.alphaTest = source.alphaTest;\n\n\t\t\tthis.premultipliedAlpha = source.premultipliedAlpha;\n\n\t\t\tthis.overdraw = source.overdraw;\n\n\t\t\tthis.visible = source.visible;\n\t\t\tthis.clipShadows = source.clipShadows;\n\t\t\tthis.clipIntersection = source.clipIntersection;\n\n\t\t\tvar srcPlanes = source.clippingPlanes,\n\t\t\t\tdstPlanes = null;\n\n\t\t\tif ( srcPlanes !== null ) {\n\n\t\t\t\tvar n = srcPlanes.length;\n\t\t\t\tdstPlanes = new Array( n );\n\n\t\t\t\tfor ( var i = 0; i !== n; ++ i )\n\t\t\t\t\tdstPlanes[ i ] = srcPlanes[ i ].clone();\n\n\t\t\t}\n\n\t\t\tthis.clippingPlanes = dstPlanes;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tupdate: function () {\n\n\t\t\tthis.dispatchEvent( { type: 'update' } );\n\n\t\t},\n\n\t\tdispose: function () {\n\n\t\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t\t}\n\n\t};\n\n\tObject.assign( Material.prototype, EventDispatcher.prototype );\n\n\tvar count$1 = 0;\n\tfunction MaterialIdCount() { return count$1++; }\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * defines: { \"label\" : \"value\" },\n\t * uniforms: { \"parameter1\": { value: 1.0 }, \"parameter2\": { value2: 2 } },\n\t *\n\t * fragmentShader: <string>,\n\t * vertexShader: <string>,\n\t *\n\t * wireframe: <boolean>,\n\t * wireframeLinewidth: <float>,\n\t *\n\t * lights: <bool>,\n\t *\n\t * skinning: <bool>,\n\t * morphTargets: <bool>,\n\t * morphNormals: <bool>\n\t * }\n\t */\n\n\tfunction ShaderMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'ShaderMaterial';\n\n\t\tthis.defines = {};\n\t\tthis.uniforms = {};\n\n\t\tthis.vertexShader = 'void main() {\\n\\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\\n}';\n\t\tthis.fragmentShader = 'void main() {\\n\\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\\n}';\n\n\t\tthis.linewidth = 1;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.fog = false; // set to use scene fog\n\t\tthis.lights = false; // set to use scene lights\n\t\tthis.clipping = false; // set to use user-defined clipping planes\n\n\t\tthis.skinning = false; // set to use skinning attribute streams\n\t\tthis.morphTargets = false; // set to use morph targets\n\t\tthis.morphNormals = false; // set to use morph normals\n\n\t\tthis.extensions = {\n\t\t\tderivatives: false, // set to use derivatives\n\t\t\tfragDepth: false, // set to use fragment depth values\n\t\t\tdrawBuffers: false, // set to use draw buffers\n\t\t\tshaderTextureLOD: false // set to use shader texture LOD\n\t\t};\n\n\t\t// When rendered geometry doesn't include these attributes but the material does,\n\t\t// use these default values in WebGL. This avoids errors when buffer data is missing.\n\t\tthis.defaultAttributeValues = {\n\t\t\t'color': [ 1, 1, 1 ],\n\t\t\t'uv': [ 0, 0 ],\n\t\t\t'uv2': [ 0, 0 ]\n\t\t};\n\n\t\tthis.index0AttributeName = undefined;\n\n\t\tif ( parameters !== undefined ) {\n\n\t\t\tif ( parameters.attributes !== undefined ) {\n\n\t\t\t\tconsole.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );\n\n\t\t\t}\n\n\t\t\tthis.setValues( parameters );\n\n\t\t}\n\n\t}\n\n\tShaderMaterial.prototype = Object.create( Material.prototype );\n\tShaderMaterial.prototype.constructor = ShaderMaterial;\n\n\tShaderMaterial.prototype.isShaderMaterial = true;\n\n\tShaderMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.fragmentShader = source.fragmentShader;\n\t\tthis.vertexShader = source.vertexShader;\n\n\t\tthis.uniforms = UniformsUtils.clone( source.uniforms );\n\n\t\tthis.defines = source.defines;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\tthis.lights = source.lights;\n\t\tthis.clipping = source.clipping;\n\n\t\tthis.skinning = source.skinning;\n\n\t\tthis.morphTargets = source.morphTargets;\n\t\tthis.morphNormals = source.morphNormals;\n\n\t\tthis.extensions = source.extensions;\n\n\t\treturn this;\n\n\t};\n\n\tShaderMaterial.prototype.toJSON = function ( meta ) {\n\n\t\tvar data = Material.prototype.toJSON.call( this, meta );\n\n\t\tdata.uniforms = this.uniforms;\n\t\tdata.vertexShader = this.vertexShader;\n\t\tdata.fragmentShader = this.fragmentShader;\n\n\t\treturn data;\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author bhouston / https://clara.io\n\t * @author WestLangley / http://github.com/WestLangley\n\t *\n\t * parameters = {\n\t *\n\t * opacity: <float>,\n\t *\n\t * map: new THREE.Texture( <Image> ),\n\t *\n\t * alphaMap: new THREE.Texture( <Image> ),\n\t *\n\t * displacementMap: new THREE.Texture( <Image> ),\n\t * displacementScale: <float>,\n\t * displacementBias: <float>,\n\t *\n\t * wireframe: <boolean>,\n\t * wireframeLinewidth: <float>\n\t * }\n\t */\n\n\tfunction MeshDepthMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'MeshDepthMaterial';\n\n\t\tthis.depthPacking = BasicDepthPacking;\n\n\t\tthis.skinning = false;\n\t\tthis.morphTargets = false;\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.fog = false;\n\t\tthis.lights = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tMeshDepthMaterial.prototype = Object.create( Material.prototype );\n\tMeshDepthMaterial.prototype.constructor = MeshDepthMaterial;\n\n\tMeshDepthMaterial.prototype.isMeshDepthMaterial = true;\n\n\tMeshDepthMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.depthPacking = source.depthPacking;\n\n\t\tthis.skinning = source.skinning;\n\t\tthis.morphTargets = source.morphTargets;\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author bhouston / http://clara.io\n\t * @author WestLangley / http://github.com/WestLangley\n\t */\n\n\tfunction Box3( min, max ) {\n\n\t\tthis.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );\n\t\tthis.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );\n\n\t}\n\n\tBox3.prototype = {\n\n\t\tconstructor: Box3,\n\n\t\tisBox3: true,\n\n\t\tset: function ( min, max ) {\n\n\t\t\tthis.min.copy( min );\n\t\t\tthis.max.copy( max );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromArray: function ( array ) {\n\n\t\t\tvar minX = + Infinity;\n\t\t\tvar minY = + Infinity;\n\t\t\tvar minZ = + Infinity;\n\n\t\t\tvar maxX = - Infinity;\n\t\t\tvar maxY = - Infinity;\n\t\t\tvar maxZ = - Infinity;\n\n\t\t\tfor ( var i = 0, l = array.length; i < l; i += 3 ) {\n\n\t\t\t\tvar x = array[ i ];\n\t\t\t\tvar y = array[ i + 1 ];\n\t\t\t\tvar z = array[ i + 2 ];\n\n\t\t\t\tif ( x < minX ) minX = x;\n\t\t\t\tif ( y < minY ) minY = y;\n\t\t\t\tif ( z < minZ ) minZ = z;\n\n\t\t\t\tif ( x > maxX ) maxX = x;\n\t\t\t\tif ( y > maxY ) maxY = y;\n\t\t\t\tif ( z > maxZ ) maxZ = z;\n\n\t\t\t}\n\n\t\t\tthis.min.set( minX, minY, minZ );\n\t\t\tthis.max.set( maxX, maxY, maxZ );\n\n\t\t},\n\n\t\tsetFromPoints: function ( points ) {\n\n\t\t\tthis.makeEmpty();\n\n\t\t\tfor ( var i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\t\tthis.expandByPoint( points[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromCenterAndSize: function () {\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function setFromCenterAndSize( center, size ) {\n\n\t\t\t\tvar halfSize = v1.copy( size ).multiplyScalar( 0.5 );\n\n\t\t\t\tthis.min.copy( center ).sub( halfSize );\n\t\t\t\tthis.max.copy( center ).add( halfSize );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tsetFromObject: function () {\n\n\t\t\t// Computes the world-axis-aligned bounding box of an object (including its children),\n\t\t\t// accounting for both the object's, and children's, world transforms\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function setFromObject( object ) {\n\n\t\t\t\tvar scope = this;\n\n\t\t\t\tobject.updateMatrixWorld( true );\n\n\t\t\t\tthis.makeEmpty();\n\n\t\t\t\tobject.traverse( function ( node ) {\n\n\t\t\t\t\tvar geometry = node.geometry;\n\n\t\t\t\t\tif ( geometry !== undefined ) {\n\n\t\t\t\t\t\tif ( (geometry && geometry.isGeometry) ) {\n\n\t\t\t\t\t\t\tvar vertices = geometry.vertices;\n\n\t\t\t\t\t\t\tfor ( var i = 0, il = vertices.length; i < il; i ++ ) {\n\n\t\t\t\t\t\t\t\tv1.copy( vertices[ i ] );\n\t\t\t\t\t\t\t\tv1.applyMatrix4( node.matrixWorld );\n\n\t\t\t\t\t\t\t\tscope.expandByPoint( v1 );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else if ( (geometry && geometry.isBufferGeometry) ) {\n\n\t\t\t\t\t\t\tvar attribute = geometry.attributes.position;\n\n\t\t\t\t\t\t\tif ( attribute !== undefined ) {\n\n\t\t\t\t\t\t\t\tvar array, offset, stride;\n\n\t\t\t\t\t\t\t\tif ( (attribute && attribute.isInterleavedBufferAttribute) ) {\n\n\t\t\t\t\t\t\t\t\tarray = attribute.data.array;\n\t\t\t\t\t\t\t\t\toffset = attribute.offset;\n\t\t\t\t\t\t\t\t\tstride = attribute.data.stride;\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tarray = attribute.array;\n\t\t\t\t\t\t\t\t\toffset = 0;\n\t\t\t\t\t\t\t\t\tstride = 3;\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tfor ( var i = offset, il = array.length; i < il; i += stride ) {\n\n\t\t\t\t\t\t\t\t\tv1.fromArray( array, i );\n\t\t\t\t\t\t\t\t\tv1.applyMatrix4( node.matrixWorld );\n\n\t\t\t\t\t\t\t\t\tscope.expandByPoint( v1 );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( box ) {\n\n\t\t\tthis.min.copy( box.min );\n\t\t\tthis.max.copy( box.max );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tmakeEmpty: function () {\n\n\t\t\tthis.min.x = this.min.y = this.min.z = + Infinity;\n\t\t\tthis.max.x = this.max.y = this.max.z = - Infinity;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tisEmpty: function () {\n\n\t\t\t// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n\t\t\treturn ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );\n\n\t\t},\n\n\t\tgetCenter: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn this.isEmpty() ? result.set( 0, 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n\t\t},\n\n\t\tgetSize: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn this.isEmpty() ? result.set( 0, 0, 0 ) : result.subVectors( this.max, this.min );\n\n\t\t},\n\n\t\texpandByPoint: function ( point ) {\n\n\t\t\tthis.min.min( point );\n\t\t\tthis.max.max( point );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\texpandByVector: function ( vector ) {\n\n\t\t\tthis.min.sub( vector );\n\t\t\tthis.max.add( vector );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\texpandByScalar: function ( scalar ) {\n\n\t\t\tthis.min.addScalar( - scalar );\n\t\t\tthis.max.addScalar( scalar );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcontainsPoint: function ( point ) {\n\n\t\t\tif ( point.x < this.min.x || point.x > this.max.x ||\n\t\t\t\t\t point.y < this.min.y || point.y > this.max.y ||\n\t\t\t\t\t point.z < this.min.z || point.z > this.max.z ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t},\n\n\t\tcontainsBox: function ( box ) {\n\n\t\t\tif ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&\n\t\t\t\t ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) &&\n\t\t\t\t ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) {\n\n\t\t\t\treturn true;\n\n\t\t\t}\n\n\t\t\treturn false;\n\n\t\t},\n\n\t\tgetParameter: function ( point, optionalTarget ) {\n\n\t\t\t// This can potentially have a divide by zero if the box\n\t\t\t// has a size dimension of 0.\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\treturn result.set(\n\t\t\t\t( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n\t\t\t\t( point.y - this.min.y ) / ( this.max.y - this.min.y ),\n\t\t\t\t( point.z - this.min.z ) / ( this.max.z - this.min.z )\n\t\t\t);\n\n\t\t},\n\n\t\tintersectsBox: function ( box ) {\n\n\t\t\t// using 6 splitting planes to rule out intersections.\n\n\t\t\tif ( box.max.x < this.min.x || box.min.x > this.max.x ||\n\t\t\t\t\t box.max.y < this.min.y || box.min.y > this.max.y ||\n\t\t\t\t\t box.max.z < this.min.z || box.min.z > this.max.z ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t},\n\n\t\tintersectsSphere: ( function () {\n\n\t\t\tvar closestPoint;\n\n\t\t\treturn function intersectsSphere( sphere ) {\n\n\t\t\t\tif ( closestPoint === undefined ) closestPoint = new Vector3();\n\n\t\t\t\t// Find the point on the AABB closest to the sphere center.\n\t\t\t\tthis.clampPoint( sphere.center, closestPoint );\n\n\t\t\t\t// If that point is inside the sphere, the AABB and sphere intersect.\n\t\t\t\treturn closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );\n\n\t\t\t};\n\n\t\t} )(),\n\n\t\tintersectsPlane: function ( plane ) {\n\n\t\t\t// We compute the minimum and maximum dot product values. If those values\n\t\t\t// are on the same side (back or front) of the plane, then there is no intersection.\n\n\t\t\tvar min, max;\n\n\t\t\tif ( plane.normal.x > 0 ) {\n\n\t\t\t\tmin = plane.normal.x * this.min.x;\n\t\t\t\tmax = plane.normal.x * this.max.x;\n\n\t\t\t} else {\n\n\t\t\t\tmin = plane.normal.x * this.max.x;\n\t\t\t\tmax = plane.normal.x * this.min.x;\n\n\t\t\t}\n\n\t\t\tif ( plane.normal.y > 0 ) {\n\n\t\t\t\tmin += plane.normal.y * this.min.y;\n\t\t\t\tmax += plane.normal.y * this.max.y;\n\n\t\t\t} else {\n\n\t\t\t\tmin += plane.normal.y * this.max.y;\n\t\t\t\tmax += plane.normal.y * this.min.y;\n\n\t\t\t}\n\n\t\t\tif ( plane.normal.z > 0 ) {\n\n\t\t\t\tmin += plane.normal.z * this.min.z;\n\t\t\t\tmax += plane.normal.z * this.max.z;\n\n\t\t\t} else {\n\n\t\t\t\tmin += plane.normal.z * this.max.z;\n\t\t\t\tmax += plane.normal.z * this.min.z;\n\n\t\t\t}\n\n\t\t\treturn ( min <= plane.constant && max >= plane.constant );\n\n\t\t},\n\n\t\tclampPoint: function ( point, optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn result.copy( point ).clamp( this.min, this.max );\n\n\t\t},\n\n\t\tdistanceToPoint: function () {\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function distanceToPoint( point ) {\n\n\t\t\t\tvar clampedPoint = v1.copy( point ).clamp( this.min, this.max );\n\t\t\t\treturn clampedPoint.sub( point ).length();\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tgetBoundingSphere: function () {\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function getBoundingSphere( optionalTarget ) {\n\n\t\t\t\tvar result = optionalTarget || new Sphere();\n\n\t\t\t\tthis.getCenter( result.center );\n\n\t\t\t\tresult.radius = this.getSize( v1 ).length() * 0.5;\n\n\t\t\t\treturn result;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tintersect: function ( box ) {\n\n\t\t\tthis.min.max( box.min );\n\t\t\tthis.max.min( box.max );\n\n\t\t\t// ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.\n\t\t\tif( this.isEmpty() ) this.makeEmpty();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tunion: function ( box ) {\n\n\t\t\tthis.min.min( box.min );\n\t\t\tthis.max.max( box.max );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyMatrix4: function () {\n\n\t\t\tvar points = [\n\t\t\t\tnew Vector3(),\n\t\t\t\tnew Vector3(),\n\t\t\t\tnew Vector3(),\n\t\t\t\tnew Vector3(),\n\t\t\t\tnew Vector3(),\n\t\t\t\tnew Vector3(),\n\t\t\t\tnew Vector3(),\n\t\t\t\tnew Vector3()\n\t\t\t];\n\n\t\t\treturn function applyMatrix4( matrix ) {\n\n\t\t\t\t// transform of empty box is an empty box.\n\t\t\t\tif( this.isEmpty() ) return this;\n\n\t\t\t\t// NOTE: I am using a binary pattern to specify all 2^3 combinations below\n\t\t\t\tpoints[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000\n\t\t\t\tpoints[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001\n\t\t\t\tpoints[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010\n\t\t\t\tpoints[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011\n\t\t\t\tpoints[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100\n\t\t\t\tpoints[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101\n\t\t\t\tpoints[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110\n\t\t\t\tpoints[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );\t// 111\n\n\t\t\t\tthis.setFromPoints( points );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslate: function ( offset ) {\n\n\t\t\tthis.min.add( offset );\n\t\t\tthis.max.add( offset );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( box ) {\n\n\t\t\treturn box.min.equals( this.min ) && box.max.equals( this.max );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author bhouston / http://clara.io\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction Sphere( center, radius ) {\n\n\t\tthis.center = ( center !== undefined ) ? center : new Vector3();\n\t\tthis.radius = ( radius !== undefined ) ? radius : 0;\n\n\t}\n\n\tSphere.prototype = {\n\n\t\tconstructor: Sphere,\n\n\t\tset: function ( center, radius ) {\n\n\t\t\tthis.center.copy( center );\n\t\t\tthis.radius = radius;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromPoints: function () {\n\n\t\t\tvar box = new Box3();\n\n\t\t\treturn function setFromPoints( points, optionalCenter ) {\n\n\t\t\t\tvar center = this.center;\n\n\t\t\t\tif ( optionalCenter !== undefined ) {\n\n\t\t\t\t\tcenter.copy( optionalCenter );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tbox.setFromPoints( points ).getCenter( center );\n\n\t\t\t\t}\n\n\t\t\t\tvar maxRadiusSq = 0;\n\n\t\t\t\tfor ( var i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );\n\n\t\t\t\t}\n\n\t\t\t\tthis.radius = Math.sqrt( maxRadiusSq );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( sphere ) {\n\n\t\t\tthis.center.copy( sphere.center );\n\t\t\tthis.radius = sphere.radius;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tempty: function () {\n\n\t\t\treturn ( this.radius <= 0 );\n\n\t\t},\n\n\t\tcontainsPoint: function ( point ) {\n\n\t\t\treturn ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );\n\n\t\t},\n\n\t\tdistanceToPoint: function ( point ) {\n\n\t\t\treturn ( point.distanceTo( this.center ) - this.radius );\n\n\t\t},\n\n\t\tintersectsSphere: function ( sphere ) {\n\n\t\t\tvar radiusSum = this.radius + sphere.radius;\n\n\t\t\treturn sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );\n\n\t\t},\n\n\t\tintersectsBox: function ( box ) {\n\n\t\t\treturn box.intersectsSphere( this );\n\n\t\t},\n\n\t\tintersectsPlane: function ( plane ) {\n\n\t\t\t// We use the following equation to compute the signed distance from\n\t\t\t// the center of the sphere to the plane.\n\t\t\t//\n\t\t\t// distance = q * n - d\n\t\t\t//\n\t\t\t// If this distance is greater than the radius of the sphere,\n\t\t\t// then there is no intersection.\n\n\t\t\treturn Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius;\n\n\t\t},\n\n\t\tclampPoint: function ( point, optionalTarget ) {\n\n\t\t\tvar deltaLengthSq = this.center.distanceToSquared( point );\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\tresult.copy( point );\n\n\t\t\tif ( deltaLengthSq > ( this.radius * this.radius ) ) {\n\n\t\t\t\tresult.sub( this.center ).normalize();\n\t\t\t\tresult.multiplyScalar( this.radius ).add( this.center );\n\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t},\n\n\t\tgetBoundingBox: function ( optionalTarget ) {\n\n\t\t\tvar box = optionalTarget || new Box3();\n\n\t\t\tbox.set( this.center, this.center );\n\t\t\tbox.expandByScalar( this.radius );\n\n\t\t\treturn box;\n\n\t\t},\n\n\t\tapplyMatrix4: function ( matrix ) {\n\n\t\t\tthis.center.applyMatrix4( matrix );\n\t\t\tthis.radius = this.radius * matrix.getMaxScaleOnAxis();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttranslate: function ( offset ) {\n\n\t\t\tthis.center.add( offset );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( sphere ) {\n\n\t\t\treturn sphere.center.equals( this.center ) && ( sphere.radius === this.radius );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author WestLangley / http://github.com/WestLangley\n\t * @author bhouston / http://clara.io\n\t * @author tschw\n\t */\n\n\tfunction Matrix3() {\n\n\t\tthis.elements = new Float32Array( [\n\n\t\t\t1, 0, 0,\n\t\t\t0, 1, 0,\n\t\t\t0, 0, 1\n\n\t\t] );\n\n\t\tif ( arguments.length > 0 ) {\n\n\t\t\tconsole.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );\n\n\t\t}\n\n\t}\n\n\tMatrix3.prototype = {\n\n\t\tconstructor: Matrix3,\n\n\t\tisMatrix3: true,\n\n\t\tset: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tte[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;\n\t\t\tte[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;\n\t\t\tte[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tidentity: function () {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0,\n\t\t\t\t0, 1, 0,\n\t\t\t\t0, 0, 1\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().fromArray( this.elements );\n\n\t\t},\n\n\t\tcopy: function ( m ) {\n\n\t\t\tvar me = m.elements;\n\n\t\t\tthis.set(\n\n\t\t\t\tme[ 0 ], me[ 3 ], me[ 6 ],\n\t\t\t\tme[ 1 ], me[ 4 ], me[ 7 ],\n\t\t\t\tme[ 2 ], me[ 5 ], me[ 8 ]\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromMatrix4: function( m ) {\n\n\t\t\tvar me = m.elements;\n\n\t\t\tthis.set(\n\n\t\t\t\tme[ 0 ], me[ 4 ], me[ 8 ],\n\t\t\t\tme[ 1 ], me[ 5 ], me[ 9 ],\n\t\t\t\tme[ 2 ], me[ 6 ], me[ 10 ]\n\n\t\t\t);\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tapplyToVector3Array: function () {\n\n\t\t\tvar v1;\n\n\t\t\treturn function applyToVector3Array( array, offset, length ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\t\t\t\tif ( offset === undefined ) offset = 0;\n\t\t\t\tif ( length === undefined ) length = array.length;\n\n\t\t\t\tfor ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {\n\n\t\t\t\t\tv1.fromArray( array, j );\n\t\t\t\t\tv1.applyMatrix3( this );\n\t\t\t\t\tv1.toArray( array, j );\n\n\t\t\t\t}\n\n\t\t\t\treturn array;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tapplyToBuffer: function () {\n\n\t\t\tvar v1;\n\n\t\t\treturn function applyToBuffer( buffer, offset, length ) {\n\n\t\t\t\tif ( v1 === undefined ) v1 = new Vector3();\n\t\t\t\tif ( offset === undefined ) offset = 0;\n\t\t\t\tif ( length === undefined ) length = buffer.length / buffer.itemSize;\n\n\t\t\t\tfor ( var i = 0, j = offset; i < length; i ++, j ++ ) {\n\n\t\t\t\t\tv1.x = buffer.getX( j );\n\t\t\t\t\tv1.y = buffer.getY( j );\n\t\t\t\t\tv1.z = buffer.getZ( j );\n\n\t\t\t\t\tv1.applyMatrix3( this );\n\n\t\t\t\t\tbuffer.setXYZ( j, v1.x, v1.y, v1.z );\n\n\t\t\t\t}\n\n\t\t\t\treturn buffer;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tmultiplyScalar: function ( s ) {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tte[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;\n\t\t\tte[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;\n\t\t\tte[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdeterminant: function () {\n\n\t\t\tvar te = this.elements;\n\n\t\t\tvar a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],\n\t\t\t\td = te[ 3 ], e = te[ 4 ], f = te[ 5 ],\n\t\t\t\tg = te[ 6 ], h = te[ 7 ], i = te[ 8 ];\n\n\t\t\treturn a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;\n\n\t\t},\n\n\t\tgetInverse: function ( matrix, throwOnDegenerate ) {\n\n\t\t\tif ( (matrix && matrix.isMatrix4) ) {\n\n\t\t\t\tconsole.error( \"THREE.Matrix3.getInverse no longer takes a Matrix4 argument.\" );\n\n\t\t\t}\n\n\t\t\tvar me = matrix.elements,\n\t\t\t\tte = this.elements,\n\n\t\t\t\tn11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],\n\t\t\t\tn12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],\n\t\t\t\tn13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],\n\n\t\t\t\tt11 = n33 * n22 - n32 * n23,\n\t\t\t\tt12 = n32 * n13 - n33 * n12,\n\t\t\t\tt13 = n23 * n12 - n22 * n13,\n\n\t\t\t\tdet = n11 * t11 + n21 * t12 + n31 * t13;\n\n\t\t\tif ( det === 0 ) {\n\n\t\t\t\tvar msg = \"THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0\";\n\n\t\t\t\tif ( throwOnDegenerate === true ) {\n\n\t\t\t\t\tthrow new Error( msg );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.warn( msg );\n\n\t\t\t\t}\n\n\t\t\t\treturn this.identity();\n\t\t\t}\n\n\t\t\tvar detInv = 1 / det;\n\n\t\t\tte[ 0 ] = t11 * detInv;\n\t\t\tte[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;\n\t\t\tte[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;\n\n\t\t\tte[ 3 ] = t12 * detInv;\n\t\t\tte[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;\n\t\t\tte[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;\n\n\t\t\tte[ 6 ] = t13 * detInv;\n\t\t\tte[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;\n\t\t\tte[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttranspose: function () {\n\n\t\t\tvar tmp, m = this.elements;\n\n\t\t\ttmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;\n\t\t\ttmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;\n\t\t\ttmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tflattenToArrayOffset: function ( array, offset ) {\n\n\t\t\tconsole.warn( \"THREE.Matrix3: .flattenToArrayOffset is deprecated \" +\n\t\t\t\t\t\"- just use .toArray instead.\" );\n\n\t\t\treturn this.toArray( array, offset );\n\n\t\t},\n\n\t\tgetNormalMatrix: function ( matrix4 ) {\n\n\t\t\treturn this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();\n\n\t\t},\n\n\t\ttransposeIntoArray: function ( r ) {\n\n\t\t\tvar m = this.elements;\n\n\t\t\tr[ 0 ] = m[ 0 ];\n\t\t\tr[ 1 ] = m[ 3 ];\n\t\t\tr[ 2 ] = m[ 6 ];\n\t\t\tr[ 3 ] = m[ 1 ];\n\t\t\tr[ 4 ] = m[ 4 ];\n\t\t\tr[ 5 ] = m[ 7 ];\n\t\t\tr[ 6 ] = m[ 2 ];\n\t\t\tr[ 7 ] = m[ 5 ];\n\t\t\tr[ 8 ] = m[ 8 ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tfromArray: function ( array, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tfor( var i = 0; i < 9; i ++ ) {\n\n\t\t\t\tthis.elements[ i ] = array[ i + offset ];\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tvar te = this.elements;\n\n\t\t\tarray[ offset ] = te[ 0 ];\n\t\t\tarray[ offset + 1 ] = te[ 1 ];\n\t\t\tarray[ offset + 2 ] = te[ 2 ];\n\n\t\t\tarray[ offset + 3 ] = te[ 3 ];\n\t\t\tarray[ offset + 4 ] = te[ 4 ];\n\t\t\tarray[ offset + 5 ] = te[ 5 ];\n\n\t\t\tarray[ offset + 6 ] = te[ 6 ];\n\t\t\tarray[ offset + 7 ] = te[ 7 ];\n\t\t\tarray[ offset + 8 ] = te[ 8 ];\n\n\t\t\treturn array;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Plane( normal, constant ) {\n\n\t\tthis.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );\n\t\tthis.constant = ( constant !== undefined ) ? constant : 0;\n\n\t}\n\n\tPlane.prototype = {\n\n\t\tconstructor: Plane,\n\n\t\tset: function ( normal, constant ) {\n\n\t\t\tthis.normal.copy( normal );\n\t\t\tthis.constant = constant;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetComponents: function ( x, y, z, w ) {\n\n\t\t\tthis.normal.set( x, y, z );\n\t\t\tthis.constant = w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromNormalAndCoplanarPoint: function ( normal, point ) {\n\n\t\t\tthis.normal.copy( normal );\n\t\t\tthis.constant = - point.dot( this.normal );\t// must be this.normal, not normal, as this.normal is normalized\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromCoplanarPoints: function () {\n\n\t\t\tvar v1 = new Vector3();\n\t\t\tvar v2 = new Vector3();\n\n\t\t\treturn function setFromCoplanarPoints( a, b, c ) {\n\n\t\t\t\tvar normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();\n\n\t\t\t\t// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?\n\n\t\t\t\tthis.setFromNormalAndCoplanarPoint( normal, a );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( plane ) {\n\n\t\t\tthis.normal.copy( plane.normal );\n\t\t\tthis.constant = plane.constant;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tnormalize: function () {\n\n\t\t\t// Note: will lead to a divide by zero if the plane is invalid.\n\n\t\t\tvar inverseNormalLength = 1.0 / this.normal.length();\n\t\t\tthis.normal.multiplyScalar( inverseNormalLength );\n\t\t\tthis.constant *= inverseNormalLength;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tnegate: function () {\n\n\t\t\tthis.constant *= - 1;\n\t\t\tthis.normal.negate();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdistanceToPoint: function ( point ) {\n\n\t\t\treturn this.normal.dot( point ) + this.constant;\n\n\t\t},\n\n\t\tdistanceToSphere: function ( sphere ) {\n\n\t\t\treturn this.distanceToPoint( sphere.center ) - sphere.radius;\n\n\t\t},\n\n\t\tprojectPoint: function ( point, optionalTarget ) {\n\n\t\t\treturn this.orthoPoint( point, optionalTarget ).sub( point ).negate();\n\n\t\t},\n\n\t\torthoPoint: function ( point, optionalTarget ) {\n\n\t\t\tvar perpendicularMagnitude = this.distanceToPoint( point );\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn result.copy( this.normal ).multiplyScalar( perpendicularMagnitude );\n\n\t\t},\n\n\t\tintersectLine: function () {\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function intersectLine( line, optionalTarget ) {\n\n\t\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\t\tvar direction = line.delta( v1 );\n\n\t\t\t\tvar denominator = this.normal.dot( direction );\n\n\t\t\t\tif ( denominator === 0 ) {\n\n\t\t\t\t\t// line is coplanar, return origin\n\t\t\t\t\tif ( this.distanceToPoint( line.start ) === 0 ) {\n\n\t\t\t\t\t\treturn result.copy( line.start );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// Unsure if this is the correct method to handle this case.\n\t\t\t\t\treturn undefined;\n\n\t\t\t\t}\n\n\t\t\t\tvar t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;\n\n\t\t\t\tif ( t < 0 || t > 1 ) {\n\n\t\t\t\t\treturn undefined;\n\n\t\t\t\t}\n\n\t\t\t\treturn result.copy( direction ).multiplyScalar( t ).add( line.start );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tintersectsLine: function ( line ) {\n\n\t\t\t// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.\n\n\t\t\tvar startSign = this.distanceToPoint( line.start );\n\t\t\tvar endSign = this.distanceToPoint( line.end );\n\n\t\t\treturn ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );\n\n\t\t},\n\n\t\tintersectsBox: function ( box ) {\n\n\t\t\treturn box.intersectsPlane( this );\n\n\t\t},\n\n\t\tintersectsSphere: function ( sphere ) {\n\n\t\t\treturn sphere.intersectsPlane( this );\n\n\t\t},\n\n\t\tcoplanarPoint: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn result.copy( this.normal ).multiplyScalar( - this.constant );\n\n\t\t},\n\n\t\tapplyMatrix4: function () {\n\n\t\t\tvar v1 = new Vector3();\n\t\t\tvar m1 = new Matrix3();\n\n\t\t\treturn function applyMatrix4( matrix, optionalNormalMatrix ) {\n\n\t\t\t\tvar referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix );\n\n\t\t\t\t// transform normal based on theory here:\n\t\t\t\t// http://www.songho.ca/opengl/gl_normaltransform.html\n\t\t\t\tvar normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );\n\t\t\t\tvar normal = this.normal.applyMatrix3( normalMatrix ).normalize();\n\n\t\t\t\t// recalculate constant (like in setFromNormalAndCoplanarPoint)\n\t\t\t\tthis.constant = - referencePoint.dot( normal );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslate: function ( offset ) {\n\n\t\t\tthis.constant = this.constant - offset.dot( this.normal );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( plane ) {\n\n\t\t\treturn plane.normal.equals( this.normal ) && ( plane.constant === this.constant );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Frustum( p0, p1, p2, p3, p4, p5 ) {\n\n\t\tthis.planes = [\n\n\t\t\t( p0 !== undefined ) ? p0 : new Plane(),\n\t\t\t( p1 !== undefined ) ? p1 : new Plane(),\n\t\t\t( p2 !== undefined ) ? p2 : new Plane(),\n\t\t\t( p3 !== undefined ) ? p3 : new Plane(),\n\t\t\t( p4 !== undefined ) ? p4 : new Plane(),\n\t\t\t( p5 !== undefined ) ? p5 : new Plane()\n\n\t\t];\n\n\t}\n\n\tFrustum.prototype = {\n\n\t\tconstructor: Frustum,\n\n\t\tset: function ( p0, p1, p2, p3, p4, p5 ) {\n\n\t\t\tvar planes = this.planes;\n\n\t\t\tplanes[ 0 ].copy( p0 );\n\t\t\tplanes[ 1 ].copy( p1 );\n\t\t\tplanes[ 2 ].copy( p2 );\n\t\t\tplanes[ 3 ].copy( p3 );\n\t\t\tplanes[ 4 ].copy( p4 );\n\t\t\tplanes[ 5 ].copy( p5 );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( frustum ) {\n\n\t\t\tvar planes = this.planes;\n\n\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\tplanes[ i ].copy( frustum.planes[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromMatrix: function ( m ) {\n\n\t\t\tvar planes = this.planes;\n\t\t\tvar me = m.elements;\n\t\t\tvar me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];\n\t\t\tvar me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];\n\t\t\tvar me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];\n\t\t\tvar me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];\n\n\t\t\tplanes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();\n\t\t\tplanes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();\n\t\t\tplanes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();\n\t\t\tplanes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();\n\t\t\tplanes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();\n\t\t\tplanes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tintersectsObject: function () {\n\n\t\t\tvar sphere = new Sphere();\n\n\t\t\treturn function intersectsObject( object ) {\n\n\t\t\t\tvar geometry = object.geometry;\n\n\t\t\t\tif ( geometry.boundingSphere === null )\n\t\t\t\t\tgeometry.computeBoundingSphere();\n\n\t\t\t\tsphere.copy( geometry.boundingSphere )\n\t\t\t\t\t.applyMatrix4( object.matrixWorld );\n\n\t\t\t\treturn this.intersectsSphere( sphere );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tintersectsSprite: function () {\n\n\t\t\tvar sphere = new Sphere();\n\n\t\t\treturn function intersectsSprite( sprite ) {\n\n\t\t\t\tsphere.center.set( 0, 0, 0 );\n\t\t\t\tsphere.radius = 0.7071067811865476;\n\t\t\t\tsphere.applyMatrix4( sprite.matrixWorld );\n\n\t\t\t\treturn this.intersectsSphere( sphere );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tintersectsSphere: function ( sphere ) {\n\n\t\t\tvar planes = this.planes;\n\t\t\tvar center = sphere.center;\n\t\t\tvar negRadius = - sphere.radius;\n\n\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\tvar distance = planes[ i ].distanceToPoint( center );\n\n\t\t\t\tif ( distance < negRadius ) {\n\n\t\t\t\t\treturn false;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t},\n\n\t\tintersectsBox: function () {\n\n\t\t\tvar p1 = new Vector3(),\n\t\t\t\tp2 = new Vector3();\n\n\t\t\treturn function intersectsBox( box ) {\n\n\t\t\t\tvar planes = this.planes;\n\n\t\t\t\tfor ( var i = 0; i < 6 ; i ++ ) {\n\n\t\t\t\t\tvar plane = planes[ i ];\n\n\t\t\t\t\tp1.x = plane.normal.x > 0 ? box.min.x : box.max.x;\n\t\t\t\t\tp2.x = plane.normal.x > 0 ? box.max.x : box.min.x;\n\t\t\t\t\tp1.y = plane.normal.y > 0 ? box.min.y : box.max.y;\n\t\t\t\t\tp2.y = plane.normal.y > 0 ? box.max.y : box.min.y;\n\t\t\t\t\tp1.z = plane.normal.z > 0 ? box.min.z : box.max.z;\n\t\t\t\t\tp2.z = plane.normal.z > 0 ? box.max.z : box.min.z;\n\n\t\t\t\t\tvar d1 = plane.distanceToPoint( p1 );\n\t\t\t\t\tvar d2 = plane.distanceToPoint( p2 );\n\n\t\t\t\t\t// if both outside plane, no intersection\n\n\t\t\t\t\tif ( d1 < 0 && d2 < 0 ) {\n\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\n\t\t\t};\n\n\t\t}(),\n\n\n\t\tcontainsPoint: function ( point ) {\n\n\t\t\tvar planes = this.planes;\n\n\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( planes[ i ].distanceToPoint( point ) < 0 ) {\n\n\t\t\t\t\treturn false;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLShadowMap( _renderer, _lights, _objects, capabilities ) {\n\n\t\tvar _gl = _renderer.context,\n\t\t_state = _renderer.state,\n\t\t_frustum = new Frustum(),\n\t\t_projScreenMatrix = new Matrix4(),\n\n\t\t_lightShadows = _lights.shadows,\n\n\t\t_shadowMapSize = new Vector2(),\n\t\t_maxShadowMapSize = new Vector2( capabilities.maxTextureSize, capabilities.maxTextureSize ),\n\n\t\t_lookTarget = new Vector3(),\n\t\t_lightPositionWorld = new Vector3(),\n\n\t\t_renderList = [],\n\n\t\t_MorphingFlag = 1,\n\t\t_SkinningFlag = 2,\n\n\t\t_NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,\n\n\t\t_depthMaterials = new Array( _NumberOfMaterialVariants ),\n\t\t_distanceMaterials = new Array( _NumberOfMaterialVariants ),\n\n\t\t_materialCache = {};\n\n\t\tvar cubeDirections = [\n\t\t\tnew Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),\n\t\t\tnew Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )\n\t\t];\n\n\t\tvar cubeUps = [\n\t\t\tnew Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),\n\t\t\tnew Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ),\tnew Vector3( 0, 0, - 1 )\n\t\t];\n\n\t\tvar cube2DViewPorts = [\n\t\t\tnew Vector4(), new Vector4(), new Vector4(),\n\t\t\tnew Vector4(), new Vector4(), new Vector4()\n\t\t];\n\n\t\t// init\n\n\t\tvar depthMaterialTemplate = new MeshDepthMaterial();\n\t\tdepthMaterialTemplate.depthPacking = RGBADepthPacking;\n\t\tdepthMaterialTemplate.clipping = true;\n\n\t\tvar distanceShader = ShaderLib[ \"distanceRGBA\" ];\n\t\tvar distanceUniforms = UniformsUtils.clone( distanceShader.uniforms );\n\n\t\tfor ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {\n\n\t\t\tvar useMorphing = ( i & _MorphingFlag ) !== 0;\n\t\t\tvar useSkinning = ( i & _SkinningFlag ) !== 0;\n\n\t\t\tvar depthMaterial = depthMaterialTemplate.clone();\n\t\t\tdepthMaterial.morphTargets = useMorphing;\n\t\t\tdepthMaterial.skinning = useSkinning;\n\n\t\t\t_depthMaterials[ i ] = depthMaterial;\n\n\t\t\tvar distanceMaterial = new ShaderMaterial( {\n\t\t\t\tdefines: {\n\t\t\t\t\t'USE_SHADOWMAP': ''\n\t\t\t\t},\n\t\t\t\tuniforms: distanceUniforms,\n\t\t\t\tvertexShader: distanceShader.vertexShader,\n\t\t\t\tfragmentShader: distanceShader.fragmentShader,\n\t\t\t\tmorphTargets: useMorphing,\n\t\t\t\tskinning: useSkinning,\n\t\t\t\tclipping: true\n\t\t\t} );\n\n\t\t\t_distanceMaterials[ i ] = distanceMaterial;\n\n\t\t}\n\n\t\t//\n\n\t\tvar scope = this;\n\n\t\tthis.enabled = false;\n\n\t\tthis.autoUpdate = true;\n\t\tthis.needsUpdate = false;\n\n\t\tthis.type = PCFShadowMap;\n\n\t\tthis.renderReverseSided = true;\n\t\tthis.renderSingleSided = true;\n\n\t\tthis.render = function ( scene, camera ) {\n\n\t\t\tif ( scope.enabled === false ) return;\n\t\t\tif ( scope.autoUpdate === false && scope.needsUpdate === false ) return;\n\n\t\t\tif ( _lightShadows.length === 0 ) return;\n\n\t\t\t// Set GL state for depth map.\n\t\t\t_state.clearColor( 1, 1, 1, 1 );\n\t\t\t_state.disable( _gl.BLEND );\n\t\t\t_state.setDepthTest( true );\n\t\t\t_state.setScissorTest( false );\n\n\t\t\t// render depth map\n\n\t\t\tvar faceCount, isPointLight;\n\n\t\t\tfor ( var i = 0, il = _lightShadows.length; i < il; i ++ ) {\n\n\t\t\t\tvar light = _lightShadows[ i ];\n\t\t\t\tvar shadow = light.shadow;\n\n\t\t\t\tif ( shadow === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );\n\t\t\t\t\tcontinue;\n\n\t\t\t\t}\n\n\t\t\t\tvar shadowCamera = shadow.camera;\n\n\t\t\t\t_shadowMapSize.copy( shadow.mapSize );\n\t\t\t\t_shadowMapSize.min( _maxShadowMapSize );\n\n\t\t\t\tif ( (light && light.isPointLight) ) {\n\n\t\t\t\t\tfaceCount = 6;\n\t\t\t\t\tisPointLight = true;\n\n\t\t\t\t\tvar vpWidth = _shadowMapSize.x;\n\t\t\t\t\tvar vpHeight = _shadowMapSize.y;\n\n\t\t\t\t\t// These viewports map a cube-map onto a 2D texture with the\n\t\t\t\t\t// following orientation:\n\t\t\t\t\t//\n\t\t\t\t\t// xzXZ\n\t\t\t\t\t// y Y\n\t\t\t\t\t//\n\t\t\t\t\t// X - Positive x direction\n\t\t\t\t\t// x - Negative x direction\n\t\t\t\t\t// Y - Positive y direction\n\t\t\t\t\t// y - Negative y direction\n\t\t\t\t\t// Z - Positive z direction\n\t\t\t\t\t// z - Negative z direction\n\n\t\t\t\t\t// positive X\n\t\t\t\t\tcube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );\n\t\t\t\t\t// negative X\n\t\t\t\t\tcube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );\n\t\t\t\t\t// positive Z\n\t\t\t\t\tcube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );\n\t\t\t\t\t// negative Z\n\t\t\t\t\tcube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );\n\t\t\t\t\t// positive Y\n\t\t\t\t\tcube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );\n\t\t\t\t\t// negative Y\n\t\t\t\t\tcube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );\n\n\t\t\t\t\t_shadowMapSize.x *= 4.0;\n\t\t\t\t\t_shadowMapSize.y *= 2.0;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tfaceCount = 1;\n\t\t\t\t\tisPointLight = false;\n\n\t\t\t\t}\n\n\t\t\t\tif ( shadow.map === null ) {\n\n\t\t\t\t\tvar pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };\n\n\t\t\t\t\tshadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );\n\n\t\t\t\t\tshadowCamera.updateProjectionMatrix();\n\n\t\t\t\t}\n\n\t\t\t\tif ( (shadow && shadow.isSpotLightShadow) ) {\n\n\t\t\t\t\tshadow.update( light );\n\n\t\t\t\t}\n\n\t\t\t\tvar shadowMap = shadow.map;\n\t\t\t\tvar shadowMatrix = shadow.matrix;\n\n\t\t\t\t_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tshadowCamera.position.copy( _lightPositionWorld );\n\n\t\t\t\t_renderer.setRenderTarget( shadowMap );\n\t\t\t\t_renderer.clear();\n\n\t\t\t\t// render shadow map for each cube face (if omni-directional) or\n\t\t\t\t// run a single pass if not\n\n\t\t\t\tfor ( var face = 0; face < faceCount; face ++ ) {\n\n\t\t\t\t\tif ( isPointLight ) {\n\n\t\t\t\t\t\t_lookTarget.copy( shadowCamera.position );\n\t\t\t\t\t\t_lookTarget.add( cubeDirections[ face ] );\n\t\t\t\t\t\tshadowCamera.up.copy( cubeUps[ face ] );\n\t\t\t\t\t\tshadowCamera.lookAt( _lookTarget );\n\n\t\t\t\t\t\tvar vpDimensions = cube2DViewPorts[ face ];\n\t\t\t\t\t\t_state.viewport( vpDimensions );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t_lookTarget.setFromMatrixPosition( light.target.matrixWorld );\n\t\t\t\t\t\tshadowCamera.lookAt( _lookTarget );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tshadowCamera.updateMatrixWorld();\n\t\t\t\t\tshadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );\n\n\t\t\t\t\t// compute shadow matrix\n\n\t\t\t\t\tshadowMatrix.set(\n\t\t\t\t\t\t0.5, 0.0, 0.0, 0.5,\n\t\t\t\t\t\t0.0, 0.5, 0.0, 0.5,\n\t\t\t\t\t\t0.0, 0.0, 0.5, 0.5,\n\t\t\t\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t\t\t\t);\n\n\t\t\t\t\tshadowMatrix.multiply( shadowCamera.projectionMatrix );\n\t\t\t\t\tshadowMatrix.multiply( shadowCamera.matrixWorldInverse );\n\n\t\t\t\t\t// update camera matrices and frustum\n\n\t\t\t\t\t_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );\n\t\t\t\t\t_frustum.setFromMatrix( _projScreenMatrix );\n\n\t\t\t\t\t// set object matrices & frustum culling\n\n\t\t\t\t\t_renderList.length = 0;\n\n\t\t\t\t\tprojectObject( scene, camera, shadowCamera );\n\n\t\t\t\t\t// render shadow map\n\t\t\t\t\t// render regular objects\n\n\t\t\t\t\tfor ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {\n\n\t\t\t\t\t\tvar object = _renderList[ j ];\n\t\t\t\t\t\tvar geometry = _objects.update( object );\n\t\t\t\t\t\tvar material = object.material;\n\n\t\t\t\t\t\tif ( (material && material.isMultiMaterial) ) {\n\n\t\t\t\t\t\t\tvar groups = geometry.groups;\n\t\t\t\t\t\t\tvar materials = material.materials;\n\n\t\t\t\t\t\t\tfor ( var k = 0, kl = groups.length; k < kl; k ++ ) {\n\n\t\t\t\t\t\t\t\tvar group = groups[ k ];\n\t\t\t\t\t\t\t\tvar groupMaterial = materials[ group.materialIndex ];\n\n\t\t\t\t\t\t\t\tif ( groupMaterial.visible === true ) {\n\n\t\t\t\t\t\t\t\t\tvar depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld );\n\t\t\t\t\t\t\t\t\t_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tvar depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld );\n\t\t\t\t\t\t\t_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Restore GL state.\n\t\t\tvar clearColor = _renderer.getClearColor(),\n\t\t\tclearAlpha = _renderer.getClearAlpha();\n\t\t\t_renderer.setClearColor( clearColor, clearAlpha );\n\n\t\t\tscope.needsUpdate = false;\n\n\t\t};\n\n\t\tfunction getDepthMaterial( object, material, isPointLight, lightPositionWorld ) {\n\n\t\t\tvar geometry = object.geometry;\n\n\t\t\tvar result = null;\n\n\t\t\tvar materialVariants = _depthMaterials;\n\t\t\tvar customMaterial = object.customDepthMaterial;\n\n\t\t\tif ( isPointLight ) {\n\n\t\t\t\tmaterialVariants = _distanceMaterials;\n\t\t\t\tcustomMaterial = object.customDistanceMaterial;\n\n\t\t\t}\n\n\t\t\tif ( ! customMaterial ) {\n\n\t\t\t\tvar useMorphing = false;\n\n\t\t\t\tif ( material.morphTargets ) {\n\n\t\t\t\t\tif ( (geometry && geometry.isBufferGeometry) ) {\n\n\t\t\t\t\t\tuseMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0;\n\n\t\t\t\t\t} else if ( (geometry && geometry.isGeometry) ) {\n\n\t\t\t\t\t\tuseMorphing = geometry.morphTargets && geometry.morphTargets.length > 0;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tvar useSkinning = object.isSkinnedMesh && material.skinning;\n\n\t\t\t\tvar variantIndex = 0;\n\n\t\t\t\tif ( useMorphing ) variantIndex |= _MorphingFlag;\n\t\t\t\tif ( useSkinning ) variantIndex |= _SkinningFlag;\n\n\t\t\t\tresult = materialVariants[ variantIndex ];\n\n\t\t\t} else {\n\n\t\t\t\tresult = customMaterial;\n\n\t\t\t}\n\n\t\t\tif ( _renderer.localClippingEnabled &&\n\t\t\t\t material.clipShadows === true &&\n\t\t\t\t\tmaterial.clippingPlanes.length !== 0 ) {\n\n\t\t\t\t// in this case we need a unique material instance reflecting the\n\t\t\t\t// appropriate state\n\n\t\t\t\tvar keyA = result.uuid, keyB = material.uuid;\n\n\t\t\t\tvar materialsForVariant = _materialCache[ keyA ];\n\n\t\t\t\tif ( materialsForVariant === undefined ) {\n\n\t\t\t\t\tmaterialsForVariant = {};\n\t\t\t\t\t_materialCache[ keyA ] = materialsForVariant;\n\n\t\t\t\t}\n\n\t\t\t\tvar cachedMaterial = materialsForVariant[ keyB ];\n\n\t\t\t\tif ( cachedMaterial === undefined ) {\n\n\t\t\t\t\tcachedMaterial = result.clone();\n\t\t\t\t\tmaterialsForVariant[ keyB ] = cachedMaterial;\n\n\t\t\t\t}\n\n\t\t\t\tresult = cachedMaterial;\n\n\t\t\t}\n\n\t\t\tresult.visible = material.visible;\n\t\t\tresult.wireframe = material.wireframe;\n\n\t\t\tvar side = material.side;\n\n\t\t\tif ( scope.renderSingleSided && side == DoubleSide ) {\n\n\t\t\t\tside = FrontSide;\n\n\t\t\t}\n\n\t\t\tif ( scope.renderReverseSided ) {\n\n\t\t\t\tif ( side === FrontSide ) side = BackSide;\n\t\t\t\telse if ( side === BackSide ) side = FrontSide;\n\n\t\t\t}\n\n\t\t\tresult.side = side;\n\n\t\t\tresult.clipShadows = material.clipShadows;\n\t\t\tresult.clippingPlanes = material.clippingPlanes;\n\n\t\t\tresult.wireframeLinewidth = material.wireframeLinewidth;\n\t\t\tresult.linewidth = material.linewidth;\n\n\t\t\tif ( isPointLight && result.uniforms.lightPos !== undefined ) {\n\n\t\t\t\tresult.uniforms.lightPos.value.copy( lightPositionWorld );\n\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t}\n\n\t\tfunction projectObject( object, camera, shadowCamera ) {\n\n\t\t\tif ( object.visible === false ) return;\n\n\t\t\tvar visible = ( object.layers.mask & camera.layers.mask ) !== 0;\n\n\t\t\tif ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {\n\n\t\t\t\tif ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {\n\n\t\t\t\t\tvar material = object.material;\n\n\t\t\t\t\tif ( material.visible === true ) {\n\n\t\t\t\t\t\tobject.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );\n\t\t\t\t\t\t_renderList.push( object );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar children = object.children;\n\n\t\t\tfor ( var i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tprojectObject( children[ i ], camera, shadowCamera );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Ray( origin, direction ) {\n\n\t\tthis.origin = ( origin !== undefined ) ? origin : new Vector3();\n\t\tthis.direction = ( direction !== undefined ) ? direction : new Vector3();\n\n\t}\n\n\tRay.prototype = {\n\n\t\tconstructor: Ray,\n\n\t\tset: function ( origin, direction ) {\n\n\t\t\tthis.origin.copy( origin );\n\t\t\tthis.direction.copy( direction );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( ray ) {\n\n\t\t\tthis.origin.copy( ray.origin );\n\t\t\tthis.direction.copy( ray.direction );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tat: function ( t, optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\treturn result.copy( this.direction ).multiplyScalar( t ).add( this.origin );\n\n\t\t},\n\n\t\tlookAt: function ( v ) {\n\n\t\t\tthis.direction.copy( v ).sub( this.origin ).normalize();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\trecast: function () {\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function recast( t ) {\n\n\t\t\t\tthis.origin.copy( this.at( t, v1 ) );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclosestPointToPoint: function ( point, optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\tresult.subVectors( point, this.origin );\n\t\t\tvar directionDistance = result.dot( this.direction );\n\n\t\t\tif ( directionDistance < 0 ) {\n\n\t\t\t\treturn result.copy( this.origin );\n\n\t\t\t}\n\n\t\t\treturn result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );\n\n\t\t},\n\n\t\tdistanceToPoint: function ( point ) {\n\n\t\t\treturn Math.sqrt( this.distanceSqToPoint( point ) );\n\n\t\t},\n\n\t\tdistanceSqToPoint: function () {\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function distanceSqToPoint( point ) {\n\n\t\t\t\tvar directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );\n\n\t\t\t\t// point behind the ray\n\n\t\t\t\tif ( directionDistance < 0 ) {\n\n\t\t\t\t\treturn this.origin.distanceToSquared( point );\n\n\t\t\t\t}\n\n\t\t\t\tv1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );\n\n\t\t\t\treturn v1.distanceToSquared( point );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tdistanceSqToSegment: function () {\n\n\t\t\tvar segCenter = new Vector3();\n\t\t\tvar segDir = new Vector3();\n\t\t\tvar diff = new Vector3();\n\n\t\t\treturn function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {\n\n\t\t\t\t// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h\n\t\t\t\t// It returns the min distance between the ray and the segment\n\t\t\t\t// defined by v0 and v1\n\t\t\t\t// It can also set two optional targets :\n\t\t\t\t// - The closest point on the ray\n\t\t\t\t// - The closest point on the segment\n\n\t\t\t\tsegCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );\n\t\t\t\tsegDir.copy( v1 ).sub( v0 ).normalize();\n\t\t\t\tdiff.copy( this.origin ).sub( segCenter );\n\n\t\t\t\tvar segExtent = v0.distanceTo( v1 ) * 0.5;\n\t\t\t\tvar a01 = - this.direction.dot( segDir );\n\t\t\t\tvar b0 = diff.dot( this.direction );\n\t\t\t\tvar b1 = - diff.dot( segDir );\n\t\t\t\tvar c = diff.lengthSq();\n\t\t\t\tvar det = Math.abs( 1 - a01 * a01 );\n\t\t\t\tvar s0, s1, sqrDist, extDet;\n\n\t\t\t\tif ( det > 0 ) {\n\n\t\t\t\t\t// The ray and segment are not parallel.\n\n\t\t\t\t\ts0 = a01 * b1 - b0;\n\t\t\t\t\ts1 = a01 * b0 - b1;\n\t\t\t\t\textDet = segExtent * det;\n\n\t\t\t\t\tif ( s0 >= 0 ) {\n\n\t\t\t\t\t\tif ( s1 >= - extDet ) {\n\n\t\t\t\t\t\t\tif ( s1 <= extDet ) {\n\n\t\t\t\t\t\t\t\t// region 0\n\t\t\t\t\t\t\t\t// Minimum at interior points of ray and segment.\n\n\t\t\t\t\t\t\t\tvar invDet = 1 / det;\n\t\t\t\t\t\t\t\ts0 *= invDet;\n\t\t\t\t\t\t\t\ts1 *= invDet;\n\t\t\t\t\t\t\t\tsqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// region 1\n\n\t\t\t\t\t\t\t\ts1 = segExtent;\n\t\t\t\t\t\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\t\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// region 5\n\n\t\t\t\t\t\t\ts1 = - segExtent;\n\t\t\t\t\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( s1 <= - extDet ) {\n\n\t\t\t\t\t\t\t// region 4\n\n\t\t\t\t\t\t\ts0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );\n\t\t\t\t\t\t\ts1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t\t} else if ( s1 <= extDet ) {\n\n\t\t\t\t\t\t\t// region 3\n\n\t\t\t\t\t\t\ts0 = 0;\n\t\t\t\t\t\t\ts1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\t\t\tsqrDist = s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// region 2\n\n\t\t\t\t\t\t\ts0 = Math.max( 0, - ( a01 * segExtent + b0 ) );\n\t\t\t\t\t\t\ts1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// Ray and segment are parallel.\n\n\t\t\t\t\ts1 = ( a01 > 0 ) ? - segExtent : segExtent;\n\t\t\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t}\n\n\t\t\t\tif ( optionalPointOnRay ) {\n\n\t\t\t\t\toptionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );\n\n\t\t\t\t}\n\n\t\t\t\tif ( optionalPointOnSegment ) {\n\n\t\t\t\t\toptionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );\n\n\t\t\t\t}\n\n\t\t\t\treturn sqrDist;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tintersectSphere: function () {\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function intersectSphere( sphere, optionalTarget ) {\n\n\t\t\t\tv1.subVectors( sphere.center, this.origin );\n\t\t\t\tvar tca = v1.dot( this.direction );\n\t\t\t\tvar d2 = v1.dot( v1 ) - tca * tca;\n\t\t\t\tvar radius2 = sphere.radius * sphere.radius;\n\n\t\t\t\tif ( d2 > radius2 ) return null;\n\n\t\t\t\tvar thc = Math.sqrt( radius2 - d2 );\n\n\t\t\t\t// t0 = first intersect point - entrance on front of sphere\n\t\t\t\tvar t0 = tca - thc;\n\n\t\t\t\t// t1 = second intersect point - exit point on back of sphere\n\t\t\t\tvar t1 = tca + thc;\n\n\t\t\t\t// test to see if both t0 and t1 are behind the ray - if so, return null\n\t\t\t\tif ( t0 < 0 && t1 < 0 ) return null;\n\n\t\t\t\t// test to see if t0 is behind the ray:\n\t\t\t\t// if it is, the ray is inside the sphere, so return the second exit point scaled by t1,\n\t\t\t\t// in order to always return an intersect point that is in front of the ray.\n\t\t\t\tif ( t0 < 0 ) return this.at( t1, optionalTarget );\n\n\t\t\t\t// else t0 is in front of the ray, so return the first collision point scaled by t0\n\t\t\t\treturn this.at( t0, optionalTarget );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tintersectsSphere: function ( sphere ) {\n\n\t\t\treturn this.distanceToPoint( sphere.center ) <= sphere.radius;\n\n\t\t},\n\n\t\tdistanceToPlane: function ( plane ) {\n\n\t\t\tvar denominator = plane.normal.dot( this.direction );\n\n\t\t\tif ( denominator === 0 ) {\n\n\t\t\t\t// line is coplanar, return origin\n\t\t\t\tif ( plane.distanceToPoint( this.origin ) === 0 ) {\n\n\t\t\t\t\treturn 0;\n\n\t\t\t\t}\n\n\t\t\t\t// Null is preferable to undefined since undefined means.... it is undefined\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t\tvar t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;\n\n\t\t\t// Return if the ray never intersects the plane\n\n\t\t\treturn t >= 0 ? t : null;\n\n\t\t},\n\n\t\tintersectPlane: function ( plane, optionalTarget ) {\n\n\t\t\tvar t = this.distanceToPlane( plane );\n\n\t\t\tif ( t === null ) {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t\treturn this.at( t, optionalTarget );\n\n\t\t},\n\n\n\n\t\tintersectsPlane: function ( plane ) {\n\n\t\t\t// check if the ray lies on the plane first\n\n\t\t\tvar distToPoint = plane.distanceToPoint( this.origin );\n\n\t\t\tif ( distToPoint === 0 ) {\n\n\t\t\t\treturn true;\n\n\t\t\t}\n\n\t\t\tvar denominator = plane.normal.dot( this.direction );\n\n\t\t\tif ( denominator * distToPoint < 0 ) {\n\n\t\t\t\treturn true;\n\n\t\t\t}\n\n\t\t\t// ray origin is behind the plane (and is pointing behind it)\n\n\t\t\treturn false;\n\n\t\t},\n\n\t\tintersectBox: function ( box, optionalTarget ) {\n\n\t\t\tvar tmin, tmax, tymin, tymax, tzmin, tzmax;\n\n\t\t\tvar invdirx = 1 / this.direction.x,\n\t\t\t\tinvdiry = 1 / this.direction.y,\n\t\t\t\tinvdirz = 1 / this.direction.z;\n\n\t\t\tvar origin = this.origin;\n\n\t\t\tif ( invdirx >= 0 ) {\n\n\t\t\t\ttmin = ( box.min.x - origin.x ) * invdirx;\n\t\t\t\ttmax = ( box.max.x - origin.x ) * invdirx;\n\n\t\t\t} else {\n\n\t\t\t\ttmin = ( box.max.x - origin.x ) * invdirx;\n\t\t\t\ttmax = ( box.min.x - origin.x ) * invdirx;\n\n\t\t\t}\n\n\t\t\tif ( invdiry >= 0 ) {\n\n\t\t\t\ttymin = ( box.min.y - origin.y ) * invdiry;\n\t\t\t\ttymax = ( box.max.y - origin.y ) * invdiry;\n\n\t\t\t} else {\n\n\t\t\t\ttymin = ( box.max.y - origin.y ) * invdiry;\n\t\t\t\ttymax = ( box.min.y - origin.y ) * invdiry;\n\n\t\t\t}\n\n\t\t\tif ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;\n\n\t\t\t// These lines also handle the case where tmin or tmax is NaN\n\t\t\t// (result of 0 * Infinity). x !== x returns true if x is NaN\n\n\t\t\tif ( tymin > tmin || tmin !== tmin ) tmin = tymin;\n\n\t\t\tif ( tymax < tmax || tmax !== tmax ) tmax = tymax;\n\n\t\t\tif ( invdirz >= 0 ) {\n\n\t\t\t\ttzmin = ( box.min.z - origin.z ) * invdirz;\n\t\t\t\ttzmax = ( box.max.z - origin.z ) * invdirz;\n\n\t\t\t} else {\n\n\t\t\t\ttzmin = ( box.max.z - origin.z ) * invdirz;\n\t\t\t\ttzmax = ( box.min.z - origin.z ) * invdirz;\n\n\t\t\t}\n\n\t\t\tif ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;\n\n\t\t\tif ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;\n\n\t\t\tif ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;\n\n\t\t\t//return point closest to the ray (positive side)\n\n\t\t\tif ( tmax < 0 ) return null;\n\n\t\t\treturn this.at( tmin >= 0 ? tmin : tmax, optionalTarget );\n\n\t\t},\n\n\t\tintersectsBox: ( function () {\n\n\t\t\tvar v = new Vector3();\n\n\t\t\treturn function intersectsBox( box ) {\n\n\t\t\t\treturn this.intersectBox( box, v ) !== null;\n\n\t\t\t};\n\n\t\t} )(),\n\n\t\tintersectTriangle: function () {\n\n\t\t\t// Compute the offset origin, edges, and normal.\n\t\t\tvar diff = new Vector3();\n\t\t\tvar edge1 = new Vector3();\n\t\t\tvar edge2 = new Vector3();\n\t\t\tvar normal = new Vector3();\n\n\t\t\treturn function intersectTriangle( a, b, c, backfaceCulling, optionalTarget ) {\n\n\t\t\t\t// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h\n\n\t\t\t\tedge1.subVectors( b, a );\n\t\t\t\tedge2.subVectors( c, a );\n\t\t\t\tnormal.crossVectors( edge1, edge2 );\n\n\t\t\t\t// Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,\n\t\t\t\t// E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by\n\t\t\t\t// |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))\n\t\t\t\t// |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))\n\t\t\t\t// |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)\n\t\t\t\tvar DdN = this.direction.dot( normal );\n\t\t\t\tvar sign;\n\n\t\t\t\tif ( DdN > 0 ) {\n\n\t\t\t\t\tif ( backfaceCulling ) return null;\n\t\t\t\t\tsign = 1;\n\n\t\t\t\t} else if ( DdN < 0 ) {\n\n\t\t\t\t\tsign = - 1;\n\t\t\t\t\tDdN = - DdN;\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t\tdiff.subVectors( this.origin, a );\n\t\t\t\tvar DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );\n\n\t\t\t\t// b1 < 0, no intersection\n\t\t\t\tif ( DdQxE2 < 0 ) {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t\tvar DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );\n\n\t\t\t\t// b2 < 0, no intersection\n\t\t\t\tif ( DdE1xQ < 0 ) {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t\t// b1+b2 > 1, no intersection\n\t\t\t\tif ( DdQxE2 + DdE1xQ > DdN ) {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t\t// Line intersects triangle, check if ray does.\n\t\t\t\tvar QdN = - sign * diff.dot( normal );\n\n\t\t\t\t// t < 0, no intersection\n\t\t\t\tif ( QdN < 0 ) {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t\t// Ray intersects triangle.\n\t\t\t\treturn this.at( QdN / DdN, optionalTarget );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tapplyMatrix4: function ( matrix4 ) {\n\n\t\t\tthis.direction.add( this.origin ).applyMatrix4( matrix4 );\n\t\t\tthis.origin.applyMatrix4( matrix4 );\n\t\t\tthis.direction.sub( this.origin );\n\t\t\tthis.direction.normalize();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( ray ) {\n\n\t\t\treturn ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author WestLangley / http://github.com/WestLangley\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Euler( x, y, z, order ) {\n\n\t\tthis._x = x || 0;\n\t\tthis._y = y || 0;\n\t\tthis._z = z || 0;\n\t\tthis._order = order || Euler.DefaultOrder;\n\n\t}\n\n\tEuler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];\n\n\tEuler.DefaultOrder = 'XYZ';\n\n\tEuler.prototype = {\n\n\t\tconstructor: Euler,\n\n\t\tisEuler: true,\n\n\t\tget x () {\n\n\t\t\treturn this._x;\n\n\t\t},\n\n\t\tset x ( value ) {\n\n\t\t\tthis._x = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tget y () {\n\n\t\t\treturn this._y;\n\n\t\t},\n\n\t\tset y ( value ) {\n\n\t\t\tthis._y = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tget z () {\n\n\t\t\treturn this._z;\n\n\t\t},\n\n\t\tset z ( value ) {\n\n\t\t\tthis._z = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tget order () {\n\n\t\t\treturn this._order;\n\n\t\t},\n\n\t\tset order ( value ) {\n\n\t\t\tthis._order = value;\n\t\t\tthis.onChangeCallback();\n\n\t\t},\n\n\t\tset: function ( x, y, z, order ) {\n\n\t\t\tthis._x = x;\n\t\t\tthis._y = y;\n\t\t\tthis._z = z;\n\t\t\tthis._order = order || this._order;\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this._x, this._y, this._z, this._order );\n\n\t\t},\n\n\t\tcopy: function ( euler ) {\n\n\t\t\tthis._x = euler._x;\n\t\t\tthis._y = euler._y;\n\t\t\tthis._z = euler._z;\n\t\t\tthis._order = euler._order;\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromRotationMatrix: function ( m, order, update ) {\n\n\t\t\tvar clamp = _Math.clamp;\n\n\t\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\t\tvar te = m.elements;\n\t\t\tvar m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];\n\t\t\tvar m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];\n\t\t\tvar m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n\t\t\torder = order || this._order;\n\n\t\t\tif ( order === 'XYZ' ) {\n\n\t\t\t\tthis._y = Math.asin( clamp( m13, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m13 ) < 0.99999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m33 );\n\t\t\t\t\tthis._z = Math.atan2( - m12, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m22 );\n\t\t\t\t\tthis._z = 0;\n\n\t\t\t\t}\n\n\t\t\t} else if ( order === 'YXZ' ) {\n\n\t\t\t\tthis._x = Math.asin( - clamp( m23, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m23 ) < 0.99999 ) {\n\n\t\t\t\t\tthis._y = Math.atan2( m13, m33 );\n\t\t\t\t\tthis._z = Math.atan2( m21, m22 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._y = Math.atan2( - m31, m11 );\n\t\t\t\t\tthis._z = 0;\n\n\t\t\t\t}\n\n\t\t\t} else if ( order === 'ZXY' ) {\n\n\t\t\t\tthis._x = Math.asin( clamp( m32, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m32 ) < 0.99999 ) {\n\n\t\t\t\t\tthis._y = Math.atan2( - m31, m33 );\n\t\t\t\t\tthis._z = Math.atan2( - m12, m22 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._y = 0;\n\t\t\t\t\tthis._z = Math.atan2( m21, m11 );\n\n\t\t\t\t}\n\n\t\t\t} else if ( order === 'ZYX' ) {\n\n\t\t\t\tthis._y = Math.asin( - clamp( m31, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m31 ) < 0.99999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m33 );\n\t\t\t\t\tthis._z = Math.atan2( m21, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = 0;\n\t\t\t\t\tthis._z = Math.atan2( - m12, m22 );\n\n\t\t\t\t}\n\n\t\t\t} else if ( order === 'YZX' ) {\n\n\t\t\t\tthis._z = Math.asin( clamp( m21, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m21 ) < 0.99999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m22 );\n\t\t\t\t\tthis._y = Math.atan2( - m31, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = 0;\n\t\t\t\t\tthis._y = Math.atan2( m13, m33 );\n\n\t\t\t\t}\n\n\t\t\t} else if ( order === 'XZY' ) {\n\n\t\t\t\tthis._z = Math.asin( - clamp( m12, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m12 ) < 0.99999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m22 );\n\t\t\t\t\tthis._y = Math.atan2( m13, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m33 );\n\t\t\t\t\tthis._y = 0;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order );\n\n\t\t\t}\n\n\t\t\tthis._order = order;\n\n\t\t\tif ( update !== false ) this.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromQuaternion: function () {\n\n\t\t\tvar matrix;\n\n\t\t\treturn function setFromQuaternion( q, order, update ) {\n\n\t\t\t\tif ( matrix === undefined ) matrix = new Matrix4();\n\n\t\t\t\tmatrix.makeRotationFromQuaternion( q );\n\n\t\t\t\treturn this.setFromRotationMatrix( matrix, order, update );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tsetFromVector3: function ( v, order ) {\n\n\t\t\treturn this.set( v.x, v.y, v.z, order || this._order );\n\n\t\t},\n\n\t\treorder: function () {\n\n\t\t\t// WARNING: this discards revolution information -bhouston\n\n\t\t\tvar q = new Quaternion();\n\n\t\t\treturn function reorder( newOrder ) {\n\n\t\t\t\tq.setFromEuler( this );\n\n\t\t\t\treturn this.setFromQuaternion( q, newOrder );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tequals: function ( euler ) {\n\n\t\t\treturn ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );\n\n\t\t},\n\n\t\tfromArray: function ( array ) {\n\n\t\t\tthis._x = array[ 0 ];\n\t\t\tthis._y = array[ 1 ];\n\t\t\tthis._z = array[ 2 ];\n\t\t\tif ( array[ 3 ] !== undefined ) this._order = array[ 3 ];\n\n\t\t\tthis.onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoArray: function ( array, offset ) {\n\n\t\t\tif ( array === undefined ) array = [];\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tarray[ offset ] = this._x;\n\t\t\tarray[ offset + 1 ] = this._y;\n\t\t\tarray[ offset + 2 ] = this._z;\n\t\t\tarray[ offset + 3 ] = this._order;\n\n\t\t\treturn array;\n\n\t\t},\n\n\t\ttoVector3: function ( optionalResult ) {\n\n\t\t\tif ( optionalResult ) {\n\n\t\t\t\treturn optionalResult.set( this._x, this._y, this._z );\n\n\t\t\t} else {\n\n\t\t\t\treturn new Vector3( this._x, this._y, this._z );\n\n\t\t\t}\n\n\t\t},\n\n\t\tonChange: function ( callback ) {\n\n\t\t\tthis.onChangeCallback = callback;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tonChangeCallback: function () {}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction Layers() {\n\n\t\tthis.mask = 1;\n\n\t}\n\n\tLayers.prototype = {\n\n\t\tconstructor: Layers,\n\n\t\tset: function ( channel ) {\n\n\t\t\tthis.mask = 1 << channel;\n\n\t\t},\n\n\t\tenable: function ( channel ) {\n\n\t\t\tthis.mask |= 1 << channel;\n\n\t\t},\n\n\t\ttoggle: function ( channel ) {\n\n\t\t\tthis.mask ^= 1 << channel;\n\n\t\t},\n\n\t\tdisable: function ( channel ) {\n\n\t\t\tthis.mask &= ~ ( 1 << channel );\n\n\t\t},\n\n\t\ttest: function ( layers ) {\n\n\t\t\treturn ( this.mask & layers.mask ) !== 0;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author WestLangley / http://github.com/WestLangley\n\t * @author elephantatwork / www.elephantatwork.ch\n\t */\n\n\tfunction Object3D() {\n\n\t\tObject.defineProperty( this, 'id', { value: Object3DIdCount() } );\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'Object3D';\n\n\t\tthis.parent = null;\n\t\tthis.children = [];\n\n\t\tthis.up = Object3D.DefaultUp.clone();\n\n\t\tvar position = new Vector3();\n\t\tvar rotation = new Euler();\n\t\tvar quaternion = new Quaternion();\n\t\tvar scale = new Vector3( 1, 1, 1 );\n\n\t\tfunction onRotationChange() {\n\n\t\t\tquaternion.setFromEuler( rotation, false );\n\n\t\t}\n\n\t\tfunction onQuaternionChange() {\n\n\t\t\trotation.setFromQuaternion( quaternion, undefined, false );\n\n\t\t}\n\n\t\trotation.onChange( onRotationChange );\n\t\tquaternion.onChange( onQuaternionChange );\n\n\t\tObject.defineProperties( this, {\n\t\t\tposition: {\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: position\n\t\t\t},\n\t\t\trotation: {\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: rotation\n\t\t\t},\n\t\t\tquaternion: {\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: quaternion\n\t\t\t},\n\t\t\tscale: {\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: scale\n\t\t\t},\n\t\t\tmodelViewMatrix: {\n\t\t\t\tvalue: new Matrix4()\n\t\t\t},\n\t\t\tnormalMatrix: {\n\t\t\t\tvalue: new Matrix3()\n\t\t\t}\n\t\t} );\n\n\t\tthis.matrix = new Matrix4();\n\t\tthis.matrixWorld = new Matrix4();\n\n\t\tthis.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;\n\t\tthis.matrixWorldNeedsUpdate = false;\n\n\t\tthis.layers = new Layers();\n\t\tthis.visible = true;\n\n\t\tthis.castShadow = false;\n\t\tthis.receiveShadow = false;\n\n\t\tthis.frustumCulled = true;\n\t\tthis.renderOrder = 0;\n\n\t\tthis.userData = {};\n\n\t\tthis.onBeforeRender = function(){}; \n\t\tthis.onAfterRender = function(){};\n\n\t}\n\n\tObject3D.DefaultUp = new Vector3( 0, 1, 0 );\n\tObject3D.DefaultMatrixAutoUpdate = true;\n\n\tObject.assign( Object3D.prototype, EventDispatcher.prototype, {\n\n\t\tisObject3D: true,\n\n\t\tapplyMatrix: function ( matrix ) {\n\n\t\t\tthis.matrix.multiplyMatrices( matrix, this.matrix );\n\n\t\t\tthis.matrix.decompose( this.position, this.quaternion, this.scale );\n\n\t\t},\n\n\t\tsetRotationFromAxisAngle: function ( axis, angle ) {\n\n\t\t\t// assumes axis is normalized\n\n\t\t\tthis.quaternion.setFromAxisAngle( axis, angle );\n\n\t\t},\n\n\t\tsetRotationFromEuler: function ( euler ) {\n\n\t\t\tthis.quaternion.setFromEuler( euler, true );\n\n\t\t},\n\n\t\tsetRotationFromMatrix: function ( m ) {\n\n\t\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\t\tthis.quaternion.setFromRotationMatrix( m );\n\n\t\t},\n\n\t\tsetRotationFromQuaternion: function ( q ) {\n\n\t\t\t// assumes q is normalized\n\n\t\t\tthis.quaternion.copy( q );\n\n\t\t},\n\n\t\trotateOnAxis: function () {\n\n\t\t\t// rotate object on axis in object space\n\t\t\t// axis is assumed to be normalized\n\n\t\t\tvar q1 = new Quaternion();\n\n\t\t\treturn function rotateOnAxis( axis, angle ) {\n\n\t\t\t\tq1.setFromAxisAngle( axis, angle );\n\n\t\t\t\tthis.quaternion.multiply( q1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\trotateX: function () {\n\n\t\t\tvar v1 = new Vector3( 1, 0, 0 );\n\n\t\t\treturn function rotateX( angle ) {\n\n\t\t\t\treturn this.rotateOnAxis( v1, angle );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\trotateY: function () {\n\n\t\t\tvar v1 = new Vector3( 0, 1, 0 );\n\n\t\t\treturn function rotateY( angle ) {\n\n\t\t\t\treturn this.rotateOnAxis( v1, angle );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\trotateZ: function () {\n\n\t\t\tvar v1 = new Vector3( 0, 0, 1 );\n\n\t\t\treturn function rotateZ( angle ) {\n\n\t\t\t\treturn this.rotateOnAxis( v1, angle );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslateOnAxis: function () {\n\n\t\t\t// translate object by distance along axis in object space\n\t\t\t// axis is assumed to be normalized\n\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function translateOnAxis( axis, distance ) {\n\n\t\t\t\tv1.copy( axis ).applyQuaternion( this.quaternion );\n\n\t\t\t\tthis.position.add( v1.multiplyScalar( distance ) );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslateX: function () {\n\n\t\t\tvar v1 = new Vector3( 1, 0, 0 );\n\n\t\t\treturn function translateX( distance ) {\n\n\t\t\t\treturn this.translateOnAxis( v1, distance );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslateY: function () {\n\n\t\t\tvar v1 = new Vector3( 0, 1, 0 );\n\n\t\t\treturn function translateY( distance ) {\n\n\t\t\t\treturn this.translateOnAxis( v1, distance );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslateZ: function () {\n\n\t\t\tvar v1 = new Vector3( 0, 0, 1 );\n\n\t\t\treturn function translateZ( distance ) {\n\n\t\t\t\treturn this.translateOnAxis( v1, distance );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tlocalToWorld: function ( vector ) {\n\n\t\t\treturn vector.applyMatrix4( this.matrixWorld );\n\n\t\t},\n\n\t\tworldToLocal: function () {\n\n\t\t\tvar m1 = new Matrix4();\n\n\t\t\treturn function worldToLocal( vector ) {\n\n\t\t\t\treturn vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tlookAt: function () {\n\n\t\t\t// This routine does not support objects with rotated and/or translated parent(s)\n\n\t\t\tvar m1 = new Matrix4();\n\n\t\t\treturn function lookAt( vector ) {\n\n\t\t\t\tm1.lookAt( vector, this.position, this.up );\n\n\t\t\t\tthis.quaternion.setFromRotationMatrix( m1 );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tadd: function ( object ) {\n\n\t\t\tif ( arguments.length > 1 ) {\n\n\t\t\t\tfor ( var i = 0; i < arguments.length; i ++ ) {\n\n\t\t\t\t\tthis.add( arguments[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\treturn this;\n\n\t\t\t}\n\n\t\t\tif ( object === this ) {\n\n\t\t\t\tconsole.error( \"THREE.Object3D.add: object can't be added as a child of itself.\", object );\n\t\t\t\treturn this;\n\n\t\t\t}\n\n\t\t\tif ( (object && object.isObject3D) ) {\n\n\t\t\t\tif ( object.parent !== null ) {\n\n\t\t\t\t\tobject.parent.remove( object );\n\n\t\t\t\t}\n\n\t\t\t\tobject.parent = this;\n\t\t\t\tobject.dispatchEvent( { type: 'added' } );\n\n\t\t\t\tthis.children.push( object );\n\n\t\t\t} else {\n\n\t\t\t\tconsole.error( \"THREE.Object3D.add: object not an instance of THREE.Object3D.\", object );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tremove: function ( object ) {\n\n\t\t\tif ( arguments.length > 1 ) {\n\n\t\t\t\tfor ( var i = 0; i < arguments.length; i ++ ) {\n\n\t\t\t\t\tthis.remove( arguments[ i ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar index = this.children.indexOf( object );\n\n\t\t\tif ( index !== - 1 ) {\n\n\t\t\t\tobject.parent = null;\n\n\t\t\t\tobject.dispatchEvent( { type: 'removed' } );\n\n\t\t\t\tthis.children.splice( index, 1 );\n\n\t\t\t}\n\n\t\t},\n\n\t\tgetObjectById: function ( id ) {\n\n\t\t\treturn this.getObjectByProperty( 'id', id );\n\n\t\t},\n\n\t\tgetObjectByName: function ( name ) {\n\n\t\t\treturn this.getObjectByProperty( 'name', name );\n\n\t\t},\n\n\t\tgetObjectByProperty: function ( name, value ) {\n\n\t\t\tif ( this[ name ] === value ) return this;\n\n\t\t\tfor ( var i = 0, l = this.children.length; i < l; i ++ ) {\n\n\t\t\t\tvar child = this.children[ i ];\n\t\t\t\tvar object = child.getObjectByProperty( name, value );\n\n\t\t\t\tif ( object !== undefined ) {\n\n\t\t\t\t\treturn object;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn undefined;\n\n\t\t},\n\n\t\tgetWorldPosition: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\tthis.updateMatrixWorld( true );\n\n\t\t\treturn result.setFromMatrixPosition( this.matrixWorld );\n\n\t\t},\n\n\t\tgetWorldQuaternion: function () {\n\n\t\t\tvar position = new Vector3();\n\t\t\tvar scale = new Vector3();\n\n\t\t\treturn function getWorldQuaternion( optionalTarget ) {\n\n\t\t\t\tvar result = optionalTarget || new Quaternion();\n\n\t\t\t\tthis.updateMatrixWorld( true );\n\n\t\t\t\tthis.matrixWorld.decompose( position, result, scale );\n\n\t\t\t\treturn result;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tgetWorldRotation: function () {\n\n\t\t\tvar quaternion = new Quaternion();\n\n\t\t\treturn function getWorldRotation( optionalTarget ) {\n\n\t\t\t\tvar result = optionalTarget || new Euler();\n\n\t\t\t\tthis.getWorldQuaternion( quaternion );\n\n\t\t\t\treturn result.setFromQuaternion( quaternion, this.rotation.order, false );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tgetWorldScale: function () {\n\n\t\t\tvar position = new Vector3();\n\t\t\tvar quaternion = new Quaternion();\n\n\t\t\treturn function getWorldScale( optionalTarget ) {\n\n\t\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\t\tthis.updateMatrixWorld( true );\n\n\t\t\t\tthis.matrixWorld.decompose( position, quaternion, result );\n\n\t\t\t\treturn result;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tgetWorldDirection: function () {\n\n\t\t\tvar quaternion = new Quaternion();\n\n\t\t\treturn function getWorldDirection( optionalTarget ) {\n\n\t\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\t\tthis.getWorldQuaternion( quaternion );\n\n\t\t\t\treturn result.set( 0, 0, 1 ).applyQuaternion( quaternion );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\traycast: function () {},\n\n\t\ttraverse: function ( callback ) {\n\n\t\t\tcallback( this );\n\n\t\t\tvar children = this.children;\n\n\t\t\tfor ( var i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tchildren[ i ].traverse( callback );\n\n\t\t\t}\n\n\t\t},\n\n\t\ttraverseVisible: function ( callback ) {\n\n\t\t\tif ( this.visible === false ) return;\n\n\t\t\tcallback( this );\n\n\t\t\tvar children = this.children;\n\n\t\t\tfor ( var i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tchildren[ i ].traverseVisible( callback );\n\n\t\t\t}\n\n\t\t},\n\n\t\ttraverseAncestors: function ( callback ) {\n\n\t\t\tvar parent = this.parent;\n\n\t\t\tif ( parent !== null ) {\n\n\t\t\t\tcallback( parent );\n\n\t\t\t\tparent.traverseAncestors( callback );\n\n\t\t\t}\n\n\t\t},\n\n\t\tupdateMatrix: function () {\n\n\t\t\tthis.matrix.compose( this.position, this.quaternion, this.scale );\n\n\t\t\tthis.matrixWorldNeedsUpdate = true;\n\n\t\t},\n\n\t\tupdateMatrixWorld: function ( force ) {\n\n\t\t\tif ( this.matrixAutoUpdate === true ) this.updateMatrix();\n\n\t\t\tif ( this.matrixWorldNeedsUpdate === true || force === true ) {\n\n\t\t\t\tif ( this.parent === null ) {\n\n\t\t\t\t\tthis.matrixWorld.copy( this.matrix );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );\n\n\t\t\t\t}\n\n\t\t\t\tthis.matrixWorldNeedsUpdate = false;\n\n\t\t\t\tforce = true;\n\n\t\t\t}\n\n\t\t\t// update children\n\n\t\t\tvar children = this.children;\n\n\t\t\tfor ( var i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tchildren[ i ].updateMatrixWorld( force );\n\n\t\t\t}\n\n\t\t},\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\t// meta is '' when called from JSON.stringify\n\t\t\tvar isRootObject = ( meta === undefined || meta === '' );\n\n\t\t\tvar output = {};\n\n\t\t\t// meta is a hash used to collect geometries, materials.\n\t\t\t// not providing it implies that this is the root object\n\t\t\t// being serialized.\n\t\t\tif ( isRootObject ) {\n\n\t\t\t\t// initialize meta obj\n\t\t\t\tmeta = {\n\t\t\t\t\tgeometries: {},\n\t\t\t\t\tmaterials: {},\n\t\t\t\t\ttextures: {},\n\t\t\t\t\timages: {}\n\t\t\t\t};\n\n\t\t\t\toutput.metadata = {\n\t\t\t\t\tversion: 4.4,\n\t\t\t\t\ttype: 'Object',\n\t\t\t\t\tgenerator: 'Object3D.toJSON'\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\t// standard Object3D serialization\n\n\t\t\tvar object = {};\n\n\t\t\tobject.uuid = this.uuid;\n\t\t\tobject.type = this.type;\n\n\t\t\tif ( this.name !== '' ) object.name = this.name;\n\t\t\tif ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;\n\t\t\tif ( this.castShadow === true ) object.castShadow = true;\n\t\t\tif ( this.receiveShadow === true ) object.receiveShadow = true;\n\t\t\tif ( this.visible === false ) object.visible = false;\n\n\t\t\tobject.matrix = this.matrix.toArray();\n\n\t\t\t//\n\n\t\t\tif ( this.geometry !== undefined ) {\n\n\t\t\t\tif ( meta.geometries[ this.geometry.uuid ] === undefined ) {\n\n\t\t\t\t\tmeta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta );\n\n\t\t\t\t}\n\n\t\t\t\tobject.geometry = this.geometry.uuid;\n\n\t\t\t}\n\n\t\t\tif ( this.material !== undefined ) {\n\n\t\t\t\tif ( meta.materials[ this.material.uuid ] === undefined ) {\n\n\t\t\t\t\tmeta.materials[ this.material.uuid ] = this.material.toJSON( meta );\n\n\t\t\t\t}\n\n\t\t\t\tobject.material = this.material.uuid;\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( this.children.length > 0 ) {\n\n\t\t\t\tobject.children = [];\n\n\t\t\t\tfor ( var i = 0; i < this.children.length; i ++ ) {\n\n\t\t\t\t\tobject.children.push( this.children[ i ].toJSON( meta ).object );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( isRootObject ) {\n\n\t\t\t\tvar geometries = extractFromCache( meta.geometries );\n\t\t\t\tvar materials = extractFromCache( meta.materials );\n\t\t\t\tvar textures = extractFromCache( meta.textures );\n\t\t\t\tvar images = extractFromCache( meta.images );\n\n\t\t\t\tif ( geometries.length > 0 ) output.geometries = geometries;\n\t\t\t\tif ( materials.length > 0 ) output.materials = materials;\n\t\t\t\tif ( textures.length > 0 ) output.textures = textures;\n\t\t\t\tif ( images.length > 0 ) output.images = images;\n\n\t\t\t}\n\n\t\t\toutput.object = object;\n\n\t\t\treturn output;\n\n\t\t\t// extract data from the cache hash\n\t\t\t// remove metadata on each item\n\t\t\t// and return as array\n\t\t\tfunction extractFromCache( cache ) {\n\n\t\t\t\tvar values = [];\n\t\t\t\tfor ( var key in cache ) {\n\n\t\t\t\t\tvar data = cache[ key ];\n\t\t\t\t\tdelete data.metadata;\n\t\t\t\t\tvalues.push( data );\n\n\t\t\t\t}\n\t\t\t\treturn values;\n\n\t\t\t}\n\n\t\t},\n\n\t\tclone: function ( recursive ) {\n\n\t\t\treturn new this.constructor().copy( this, recursive );\n\n\t\t},\n\n\t\tcopy: function ( source, recursive ) {\n\n\t\t\tif ( recursive === undefined ) recursive = true;\n\n\t\t\tthis.name = source.name;\n\n\t\t\tthis.up.copy( source.up );\n\n\t\t\tthis.position.copy( source.position );\n\t\t\tthis.quaternion.copy( source.quaternion );\n\t\t\tthis.scale.copy( source.scale );\n\n\t\t\tthis.matrix.copy( source.matrix );\n\t\t\tthis.matrixWorld.copy( source.matrixWorld );\n\n\t\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\t\t\tthis.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;\n\n\t\t\tthis.visible = source.visible;\n\n\t\t\tthis.castShadow = source.castShadow;\n\t\t\tthis.receiveShadow = source.receiveShadow;\n\n\t\t\tthis.frustumCulled = source.frustumCulled;\n\t\t\tthis.renderOrder = source.renderOrder;\n\n\t\t\tthis.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n\t\t\tif ( recursive === true ) {\n\n\t\t\t\tfor ( var i = 0; i < source.children.length; i ++ ) {\n\n\t\t\t\t\tvar child = source.children[ i ];\n\t\t\t\t\tthis.add( child.clone() );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\tvar count$2 = 0;\n\tfunction Object3DIdCount() { return count$2++; }\n\n\t/**\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Line3( start, end ) {\n\n\t\tthis.start = ( start !== undefined ) ? start : new Vector3();\n\t\tthis.end = ( end !== undefined ) ? end : new Vector3();\n\n\t}\n\n\tLine3.prototype = {\n\n\t\tconstructor: Line3,\n\n\t\tset: function ( start, end ) {\n\n\t\t\tthis.start.copy( start );\n\t\t\tthis.end.copy( end );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( line ) {\n\n\t\t\tthis.start.copy( line.start );\n\t\t\tthis.end.copy( line.end );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetCenter: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn result.addVectors( this.start, this.end ).multiplyScalar( 0.5 );\n\n\t\t},\n\n\t\tdelta: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn result.subVectors( this.end, this.start );\n\n\t\t},\n\n\t\tdistanceSq: function () {\n\n\t\t\treturn this.start.distanceToSquared( this.end );\n\n\t\t},\n\n\t\tdistance: function () {\n\n\t\t\treturn this.start.distanceTo( this.end );\n\n\t\t},\n\n\t\tat: function ( t, optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\treturn this.delta( result ).multiplyScalar( t ).add( this.start );\n\n\t\t},\n\n\t\tclosestPointToPointParameter: function () {\n\n\t\t\tvar startP = new Vector3();\n\t\t\tvar startEnd = new Vector3();\n\n\t\t\treturn function closestPointToPointParameter( point, clampToLine ) {\n\n\t\t\t\tstartP.subVectors( point, this.start );\n\t\t\t\tstartEnd.subVectors( this.end, this.start );\n\n\t\t\t\tvar startEnd2 = startEnd.dot( startEnd );\n\t\t\t\tvar startEnd_startP = startEnd.dot( startP );\n\n\t\t\t\tvar t = startEnd_startP / startEnd2;\n\n\t\t\t\tif ( clampToLine ) {\n\n\t\t\t\t\tt = _Math.clamp( t, 0, 1 );\n\n\t\t\t\t}\n\n\t\t\t\treturn t;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tclosestPointToPoint: function ( point, clampToLine, optionalTarget ) {\n\n\t\t\tvar t = this.closestPointToPointParameter( point, clampToLine );\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\treturn this.delta( result ).multiplyScalar( t ).add( this.start );\n\n\t\t},\n\n\t\tapplyMatrix4: function ( matrix ) {\n\n\t\t\tthis.start.applyMatrix4( matrix );\n\t\t\tthis.end.applyMatrix4( matrix );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tequals: function ( line ) {\n\n\t\t\treturn line.start.equals( this.start ) && line.end.equals( this.end );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author bhouston / http://clara.io\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction Triangle( a, b, c ) {\n\n\t\tthis.a = ( a !== undefined ) ? a : new Vector3();\n\t\tthis.b = ( b !== undefined ) ? b : new Vector3();\n\t\tthis.c = ( c !== undefined ) ? c : new Vector3();\n\n\t}\n\n\tTriangle.normal = function () {\n\n\t\tvar v0 = new Vector3();\n\n\t\treturn function normal( a, b, c, optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\tresult.subVectors( c, b );\n\t\t\tv0.subVectors( a, b );\n\t\t\tresult.cross( v0 );\n\n\t\t\tvar resultLengthSq = result.lengthSq();\n\t\t\tif ( resultLengthSq > 0 ) {\n\n\t\t\t\treturn result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );\n\n\t\t\t}\n\n\t\t\treturn result.set( 0, 0, 0 );\n\n\t\t};\n\n\t}();\n\n\t// static/instance method to calculate barycentric coordinates\n\t// based on: http://www.blackpawn.com/texts/pointinpoly/default.html\n\tTriangle.barycoordFromPoint = function () {\n\n\t\tvar v0 = new Vector3();\n\t\tvar v1 = new Vector3();\n\t\tvar v2 = new Vector3();\n\n\t\treturn function barycoordFromPoint( point, a, b, c, optionalTarget ) {\n\n\t\t\tv0.subVectors( c, a );\n\t\t\tv1.subVectors( b, a );\n\t\t\tv2.subVectors( point, a );\n\n\t\t\tvar dot00 = v0.dot( v0 );\n\t\t\tvar dot01 = v0.dot( v1 );\n\t\t\tvar dot02 = v0.dot( v2 );\n\t\t\tvar dot11 = v1.dot( v1 );\n\t\t\tvar dot12 = v1.dot( v2 );\n\n\t\t\tvar denom = ( dot00 * dot11 - dot01 * dot01 );\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\t// collinear or singular triangle\n\t\t\tif ( denom === 0 ) {\n\n\t\t\t\t// arbitrary location outside of triangle?\n\t\t\t\t// not sure if this is the best idea, maybe should be returning undefined\n\t\t\t\treturn result.set( - 2, - 1, - 1 );\n\n\t\t\t}\n\n\t\t\tvar invDenom = 1 / denom;\n\t\t\tvar u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;\n\t\t\tvar v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;\n\n\t\t\t// barycentric coordinates must always sum to 1\n\t\t\treturn result.set( 1 - u - v, v, u );\n\n\t\t};\n\n\t}();\n\n\tTriangle.containsPoint = function () {\n\n\t\tvar v1 = new Vector3();\n\n\t\treturn function containsPoint( point, a, b, c ) {\n\n\t\t\tvar result = Triangle.barycoordFromPoint( point, a, b, c, v1 );\n\n\t\t\treturn ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );\n\n\t\t};\n\n\t}();\n\n\tTriangle.prototype = {\n\n\t\tconstructor: Triangle,\n\n\t\tset: function ( a, b, c ) {\n\n\t\t\tthis.a.copy( a );\n\t\t\tthis.b.copy( b );\n\t\t\tthis.c.copy( c );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromPointsAndIndices: function ( points, i0, i1, i2 ) {\n\n\t\t\tthis.a.copy( points[ i0 ] );\n\t\t\tthis.b.copy( points[ i1 ] );\n\t\t\tthis.c.copy( points[ i2 ] );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( triangle ) {\n\n\t\t\tthis.a.copy( triangle.a );\n\t\t\tthis.b.copy( triangle.b );\n\t\t\tthis.c.copy( triangle.c );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tarea: function () {\n\n\t\t\tvar v0 = new Vector3();\n\t\t\tvar v1 = new Vector3();\n\n\t\t\treturn function area() {\n\n\t\t\t\tv0.subVectors( this.c, this.b );\n\t\t\t\tv1.subVectors( this.a, this.b );\n\n\t\t\t\treturn v0.cross( v1 ).length() * 0.5;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tmidpoint: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\treturn result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );\n\n\t\t},\n\n\t\tnormal: function ( optionalTarget ) {\n\n\t\t\treturn Triangle.normal( this.a, this.b, this.c, optionalTarget );\n\n\t\t},\n\n\t\tplane: function ( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Plane();\n\n\t\t\treturn result.setFromCoplanarPoints( this.a, this.b, this.c );\n\n\t\t},\n\n\t\tbarycoordFromPoint: function ( point, optionalTarget ) {\n\n\t\t\treturn Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget );\n\n\t\t},\n\n\t\tcontainsPoint: function ( point ) {\n\n\t\t\treturn Triangle.containsPoint( point, this.a, this.b, this.c );\n\n\t\t},\n\n\t\tclosestPointToPoint: function () {\n\n\t\t\tvar plane, edgeList, projectedPoint, closestPoint;\n\n\t\t\treturn function closestPointToPoint( point, optionalTarget ) {\n\n\t\t\t\tif ( plane === undefined ) {\n\n\t\t\t\t\tplane = new Plane();\n\t\t\t\t\tedgeList = [ new Line3(), new Line3(), new Line3() ];\n\t\t\t\t\tprojectedPoint = new Vector3();\n\t\t\t\t\tclosestPoint = new Vector3();\n\n\t\t\t\t}\n\n\t\t\t\tvar result = optionalTarget || new Vector3();\n\t\t\t\tvar minDistance = Infinity;\n\n\t\t\t\t// project the point onto the plane of the triangle\n\n\t\t\t\tplane.setFromCoplanarPoints( this.a, this.b, this.c );\n\t\t\t\tplane.projectPoint( point, projectedPoint );\n\n\t\t\t\t// check if the projection lies within the triangle\n\n\t\t\t\tif( this.containsPoint( projectedPoint ) === true ) {\n\n\t\t\t\t\t// if so, this is the closest point\n\n\t\t\t\t\tresult.copy( projectedPoint );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// if not, the point falls outside the triangle. the result is the closest point to the triangle's edges or vertices\n\n\t\t\t\t\tedgeList[ 0 ].set( this.a, this.b );\n\t\t\t\t\tedgeList[ 1 ].set( this.b, this.c );\n\t\t\t\t\tedgeList[ 2 ].set( this.c, this.a );\n\n\t\t\t\t\tfor( var i = 0; i < edgeList.length; i ++ ) {\n\n\t\t\t\t\t\tedgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint );\n\n\t\t\t\t\t\tvar distance = projectedPoint.distanceToSquared( closestPoint );\n\n\t\t\t\t\t\tif( distance < minDistance ) {\n\n\t\t\t\t\t\t\tminDistance = distance;\n\n\t\t\t\t\t\t\tresult.copy( closestPoint );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tequals: function ( triangle ) {\n\n\t\t\treturn triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction Face3( a, b, c, normal, color, materialIndex ) {\n\n\t\tthis.a = a;\n\t\tthis.b = b;\n\t\tthis.c = c;\n\n\t\tthis.normal = (normal && normal.isVector3) ? normal : new Vector3();\n\t\tthis.vertexNormals = Array.isArray( normal ) ? normal : [];\n\n\t\tthis.color = (color && color.isColor) ? color : new Color();\n\t\tthis.vertexColors = Array.isArray( color ) ? color : [];\n\n\t\tthis.materialIndex = materialIndex !== undefined ? materialIndex : 0;\n\n\t}\n\n\tFace3.prototype = {\n\n\t\tconstructor: Face3,\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tthis.a = source.a;\n\t\t\tthis.b = source.b;\n\t\t\tthis.c = source.c;\n\n\t\t\tthis.normal.copy( source.normal );\n\t\t\tthis.color.copy( source.color );\n\n\t\t\tthis.materialIndex = source.materialIndex;\n\n\t\t\tfor ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) {\n\n\t\t\t\tthis.vertexNormals[ i ] = source.vertexNormals[ i ].clone();\n\n\t\t\t}\n\n\t\t\tfor ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) {\n\n\t\t\t\tthis.vertexColors[ i ] = source.vertexColors[ i ].clone();\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * opacity: <float>,\n\t * map: new THREE.Texture( <Image> ),\n\t *\n\t * aoMap: new THREE.Texture( <Image> ),\n\t * aoMapIntensity: <float>\n\t *\n\t * specularMap: new THREE.Texture( <Image> ),\n\t *\n\t * alphaMap: new THREE.Texture( <Image> ),\n\t *\n\t * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),\n\t * combine: THREE.Multiply,\n\t * reflectivity: <float>,\n\t * refractionRatio: <float>,\n\t *\n\t * shading: THREE.SmoothShading,\n\t * depthTest: <bool>,\n\t * depthWrite: <bool>,\n\t *\n\t * wireframe: <boolean>,\n\t * wireframeLinewidth: <float>,\n\t *\n\t * skinning: <bool>,\n\t * morphTargets: <bool>\n\t * }\n\t */\n\n\tfunction MeshBasicMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'MeshBasicMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // emissive\n\n\t\tthis.map = null;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.specularMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.combine = MultiplyOperation;\n\t\tthis.reflectivity = 1;\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.skinning = false;\n\t\tthis.morphTargets = false;\n\n\t\tthis.lights = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tMeshBasicMaterial.prototype = Object.create( Material.prototype );\n\tMeshBasicMaterial.prototype.constructor = MeshBasicMaterial;\n\n\tMeshBasicMaterial.prototype.isMeshBasicMaterial = true;\n\n\tMeshBasicMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.specularMap = source.specularMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.combine = source.combine;\n\t\tthis.reflectivity = source.reflectivity;\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.skinning = source.skinning;\n\t\tthis.morphTargets = source.morphTargets;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction BufferAttribute( array, itemSize, normalized ) {\n\n\t\tif ( Array.isArray( array ) ) {\n\n\t\t\tthrow new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n\t\t}\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.array = array;\n\t\tthis.itemSize = itemSize;\n\t\tthis.count = array !== undefined ? array.length / itemSize : 0;\n\t\tthis.normalized = normalized === true;\n\n\t\tthis.dynamic = false;\n\t\tthis.updateRange = { offset: 0, count: - 1 };\n\n\t\tthis.version = 0;\n\n\t}\n\n\tBufferAttribute.prototype = {\n\n\t\tconstructor: BufferAttribute,\n\n\t\tisBufferAttribute: true,\n\n\t\tset needsUpdate( value ) {\n\n\t\t\tif ( value === true ) this.version ++;\n\n\t\t},\n\n\t\tsetArray: function ( array ) {\n\n\t\t\tif ( Array.isArray( array ) ) {\n\n\t\t\t\tthrow new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n\t\t\t}\n\n\t\t\tthis.count = array !== undefined ? array.length / this.itemSize : 0;\n\t\t\tthis.array = array;\n\n\t\t},\n\n\t\tsetDynamic: function ( value ) {\n\n\t\t\tthis.dynamic = value;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tthis.array = new source.array.constructor( source.array );\n\t\t\tthis.itemSize = source.itemSize;\n\t\t\tthis.count = source.count;\n\t\t\tthis.normalized = source.normalized;\n\n\t\t\tthis.dynamic = source.dynamic;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyAt: function ( index1, attribute, index2 ) {\n\n\t\t\tindex1 *= this.itemSize;\n\t\t\tindex2 *= attribute.itemSize;\n\n\t\t\tfor ( var i = 0, l = this.itemSize; i < l; i ++ ) {\n\n\t\t\t\tthis.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyArray: function ( array ) {\n\n\t\t\tthis.array.set( array );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyColorsArray: function ( colors ) {\n\n\t\t\tvar array = this.array, offset = 0;\n\n\t\t\tfor ( var i = 0, l = colors.length; i < l; i ++ ) {\n\n\t\t\t\tvar color = colors[ i ];\n\n\t\t\t\tif ( color === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );\n\t\t\t\t\tcolor = new Color();\n\n\t\t\t\t}\n\n\t\t\t\tarray[ offset ++ ] = color.r;\n\t\t\t\tarray[ offset ++ ] = color.g;\n\t\t\t\tarray[ offset ++ ] = color.b;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyIndicesArray: function ( indices ) {\n\n\t\t\tvar array = this.array, offset = 0;\n\n\t\t\tfor ( var i = 0, l = indices.length; i < l; i ++ ) {\n\n\t\t\t\tvar index = indices[ i ];\n\n\t\t\t\tarray[ offset ++ ] = index.a;\n\t\t\t\tarray[ offset ++ ] = index.b;\n\t\t\t\tarray[ offset ++ ] = index.c;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyVector2sArray: function ( vectors ) {\n\n\t\t\tvar array = this.array, offset = 0;\n\n\t\t\tfor ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n\t\t\t\tvar vector = vectors[ i ];\n\n\t\t\t\tif ( vector === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );\n\t\t\t\t\tvector = new Vector2();\n\n\t\t\t\t}\n\n\t\t\t\tarray[ offset ++ ] = vector.x;\n\t\t\t\tarray[ offset ++ ] = vector.y;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyVector3sArray: function ( vectors ) {\n\n\t\t\tvar array = this.array, offset = 0;\n\n\t\t\tfor ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n\t\t\t\tvar vector = vectors[ i ];\n\n\t\t\t\tif ( vector === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );\n\t\t\t\t\tvector = new Vector3();\n\n\t\t\t\t}\n\n\t\t\t\tarray[ offset ++ ] = vector.x;\n\t\t\t\tarray[ offset ++ ] = vector.y;\n\t\t\t\tarray[ offset ++ ] = vector.z;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcopyVector4sArray: function ( vectors ) {\n\n\t\t\tvar array = this.array, offset = 0;\n\n\t\t\tfor ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n\t\t\t\tvar vector = vectors[ i ];\n\n\t\t\t\tif ( vector === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );\n\t\t\t\t\tvector = new Vector4();\n\n\t\t\t\t}\n\n\t\t\t\tarray[ offset ++ ] = vector.x;\n\t\t\t\tarray[ offset ++ ] = vector.y;\n\t\t\t\tarray[ offset ++ ] = vector.z;\n\t\t\t\tarray[ offset ++ ] = vector.w;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tset: function ( value, offset ) {\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tthis.array.set( value, offset );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetX: function ( index ) {\n\n\t\t\treturn this.array[ index * this.itemSize ];\n\n\t\t},\n\n\t\tsetX: function ( index, x ) {\n\n\t\t\tthis.array[ index * this.itemSize ] = x;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetY: function ( index ) {\n\n\t\t\treturn this.array[ index * this.itemSize + 1 ];\n\n\t\t},\n\n\t\tsetY: function ( index, y ) {\n\n\t\t\tthis.array[ index * this.itemSize + 1 ] = y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetZ: function ( index ) {\n\n\t\t\treturn this.array[ index * this.itemSize + 2 ];\n\n\t\t},\n\n\t\tsetZ: function ( index, z ) {\n\n\t\t\tthis.array[ index * this.itemSize + 2 ] = z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetW: function ( index ) {\n\n\t\t\treturn this.array[ index * this.itemSize + 3 ];\n\n\t\t},\n\n\t\tsetW: function ( index, w ) {\n\n\t\t\tthis.array[ index * this.itemSize + 3 ] = w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetXY: function ( index, x, y ) {\n\n\t\t\tindex *= this.itemSize;\n\n\t\t\tthis.array[ index + 0 ] = x;\n\t\t\tthis.array[ index + 1 ] = y;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetXYZ: function ( index, x, y, z ) {\n\n\t\t\tindex *= this.itemSize;\n\n\t\t\tthis.array[ index + 0 ] = x;\n\t\t\tthis.array[ index + 1 ] = y;\n\t\t\tthis.array[ index + 2 ] = z;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetXYZW: function ( index, x, y, z, w ) {\n\n\t\t\tindex *= this.itemSize;\n\n\t\t\tthis.array[ index + 0 ] = x;\n\t\t\tthis.array[ index + 1 ] = y;\n\t\t\tthis.array[ index + 2 ] = z;\n\t\t\tthis.array[ index + 3 ] = w;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t}\n\n\t};\n\n\t//\n\n\tfunction Int8Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Int8Array( array ), itemSize );\n\n\t}\n\n\tfunction Uint8Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Uint8Array( array ), itemSize );\n\n\t}\n\n\tfunction Uint8ClampedAttribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Uint8ClampedArray( array ), itemSize );\n\n\t}\n\n\tfunction Int16Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Int16Array( array ), itemSize );\n\n\t}\n\n\tfunction Uint16Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Uint16Array( array ), itemSize );\n\n\t}\n\n\tfunction Int32Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Int32Array( array ), itemSize );\n\n\t}\n\n\tfunction Uint32Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Uint32Array( array ), itemSize );\n\n\t}\n\n\tfunction Float32Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Float32Array( array ), itemSize );\n\n\t}\n\n\tfunction Float64Attribute( array, itemSize ) {\n\n\t\treturn new BufferAttribute( new Float64Array( array ), itemSize );\n\n\t}\n\n\t// Deprecated\n\n\tfunction DynamicBufferAttribute( array, itemSize ) {\n\n\t\tconsole.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );\n\t\treturn new BufferAttribute( array, itemSize ).setDynamic( true );\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author kile / http://kile.stravaganza.org/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t * @author bhouston / http://clara.io\n\t */\n\n\tfunction Geometry() {\n\n\t\tObject.defineProperty( this, 'id', { value: GeometryIdCount() } );\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'Geometry';\n\n\t\tthis.vertices = [];\n\t\tthis.colors = [];\n\t\tthis.faces = [];\n\t\tthis.faceVertexUvs = [ [] ];\n\n\t\tthis.morphTargets = [];\n\t\tthis.morphNormals = [];\n\n\t\tthis.skinWeights = [];\n\t\tthis.skinIndices = [];\n\n\t\tthis.lineDistances = [];\n\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\t// update flags\n\n\t\tthis.elementsNeedUpdate = false;\n\t\tthis.verticesNeedUpdate = false;\n\t\tthis.uvsNeedUpdate = false;\n\t\tthis.normalsNeedUpdate = false;\n\t\tthis.colorsNeedUpdate = false;\n\t\tthis.lineDistancesNeedUpdate = false;\n\t\tthis.groupsNeedUpdate = false;\n\n\t}\n\n\tObject.assign( Geometry.prototype, EventDispatcher.prototype, {\n\n\t\tisGeometry: true,\n\n\t\tapplyMatrix: function ( matrix ) {\n\n\t\t\tvar normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n\t\t\tfor ( var i = 0, il = this.vertices.length; i < il; i ++ ) {\n\n\t\t\t\tvar vertex = this.vertices[ i ];\n\t\t\t\tvertex.applyMatrix4( matrix );\n\n\t\t\t}\n\n\t\t\tfor ( var i = 0, il = this.faces.length; i < il; i ++ ) {\n\n\t\t\t\tvar face = this.faces[ i ];\n\t\t\t\tface.normal.applyMatrix3( normalMatrix ).normalize();\n\n\t\t\t\tfor ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {\n\n\t\t\t\t\tface.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( this.boundingBox !== null ) {\n\n\t\t\t\tthis.computeBoundingBox();\n\n\t\t\t}\n\n\t\t\tif ( this.boundingSphere !== null ) {\n\n\t\t\t\tthis.computeBoundingSphere();\n\n\t\t\t}\n\n\t\t\tthis.verticesNeedUpdate = true;\n\t\t\tthis.normalsNeedUpdate = true;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\trotateX: function () {\n\n\t\t\t// rotate geometry around world x-axis\n\n\t\t\tvar m1;\n\n\t\t\treturn function rotateX( angle ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeRotationX( angle );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\trotateY: function () {\n\n\t\t\t// rotate geometry around world y-axis\n\n\t\t\tvar m1;\n\n\t\t\treturn function rotateY( angle ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeRotationY( angle );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\trotateZ: function () {\n\n\t\t\t// rotate geometry around world z-axis\n\n\t\t\tvar m1;\n\n\t\t\treturn function rotateZ( angle ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeRotationZ( angle );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslate: function () {\n\n\t\t\t// translate geometry\n\n\t\t\tvar m1;\n\n\t\t\treturn function translate( x, y, z ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeTranslation( x, y, z );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tscale: function () {\n\n\t\t\t// scale geometry\n\n\t\t\tvar m1;\n\n\t\t\treturn function scale( x, y, z ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeScale( x, y, z );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tlookAt: function () {\n\n\t\t\tvar obj;\n\n\t\t\treturn function lookAt( vector ) {\n\n\t\t\t\tif ( obj === undefined ) obj = new Object3D();\n\n\t\t\t\tobj.lookAt( vector );\n\n\t\t\t\tobj.updateMatrix();\n\n\t\t\t\tthis.applyMatrix( obj.matrix );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tfromBufferGeometry: function ( geometry ) {\n\n\t\t\tvar scope = this;\n\n\t\t\tvar indices = geometry.index !== null ? geometry.index.array : undefined;\n\t\t\tvar attributes = geometry.attributes;\n\n\t\t\tvar positions = attributes.position.array;\n\t\t\tvar normals = attributes.normal !== undefined ? attributes.normal.array : undefined;\n\t\t\tvar colors = attributes.color !== undefined ? attributes.color.array : undefined;\n\t\t\tvar uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;\n\t\t\tvar uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;\n\n\t\t\tif ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];\n\n\t\t\tvar tempNormals = [];\n\t\t\tvar tempUVs = [];\n\t\t\tvar tempUVs2 = [];\n\n\t\t\tfor ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {\n\n\t\t\t\tscope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) );\n\n\t\t\t\tif ( normals !== undefined ) {\n\n\t\t\t\t\ttempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( colors !== undefined ) {\n\n\t\t\t\t\tscope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( uvs !== undefined ) {\n\n\t\t\t\t\ttempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( uvs2 !== undefined ) {\n\n\t\t\t\t\ttempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction addFace( a, b, c, materialIndex ) {\n\n\t\t\t\tvar vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];\n\t\t\t\tvar vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];\n\n\t\t\t\tvar face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );\n\n\t\t\t\tscope.faces.push( face );\n\n\t\t\t\tif ( uvs !== undefined ) {\n\n\t\t\t\t\tscope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( uvs2 !== undefined ) {\n\n\t\t\t\t\tscope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( indices !== undefined ) {\n\n\t\t\t\tvar groups = geometry.groups;\n\n\t\t\t\tif ( groups.length > 0 ) {\n\n\t\t\t\t\tfor ( var i = 0; i < groups.length; i ++ ) {\n\n\t\t\t\t\t\tvar group = groups[ i ];\n\n\t\t\t\t\t\tvar start = group.start;\n\t\t\t\t\t\tvar count = group.count;\n\n\t\t\t\t\t\tfor ( var j = start, jl = start + count; j < jl; j += 3 ) {\n\n\t\t\t\t\t\t\taddFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tfor ( var i = 0; i < indices.length; i += 3 ) {\n\n\t\t\t\t\t\taddFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tfor ( var i = 0; i < positions.length / 3; i += 3 ) {\n\n\t\t\t\t\taddFace( i, i + 1, i + 2 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.computeFaceNormals();\n\n\t\t\tif ( geometry.boundingBox !== null ) {\n\n\t\t\t\tthis.boundingBox = geometry.boundingBox.clone();\n\n\t\t\t}\n\n\t\t\tif ( geometry.boundingSphere !== null ) {\n\n\t\t\t\tthis.boundingSphere = geometry.boundingSphere.clone();\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcenter: function () {\n\n\t\t\tthis.computeBoundingBox();\n\n\t\t\tvar offset = this.boundingBox.getCenter().negate();\n\n\t\t\tthis.translate( offset.x, offset.y, offset.z );\n\n\t\t\treturn offset;\n\n\t\t},\n\n\t\tnormalize: function () {\n\n\t\t\tthis.computeBoundingSphere();\n\n\t\t\tvar center = this.boundingSphere.center;\n\t\t\tvar radius = this.boundingSphere.radius;\n\n\t\t\tvar s = radius === 0 ? 1 : 1.0 / radius;\n\n\t\t\tvar matrix = new Matrix4();\n\t\t\tmatrix.set(\n\t\t\t\ts, 0, 0, - s * center.x,\n\t\t\t\t0, s, 0, - s * center.y,\n\t\t\t\t0, 0, s, - s * center.z,\n\t\t\t\t0, 0, 0, 1\n\t\t\t);\n\n\t\t\tthis.applyMatrix( matrix );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcomputeFaceNormals: function () {\n\n\t\t\tvar cb = new Vector3(), ab = new Vector3();\n\n\t\t\tfor ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\tvar face = this.faces[ f ];\n\n\t\t\t\tvar vA = this.vertices[ face.a ];\n\t\t\t\tvar vB = this.vertices[ face.b ];\n\t\t\t\tvar vC = this.vertices[ face.c ];\n\n\t\t\t\tcb.subVectors( vC, vB );\n\t\t\t\tab.subVectors( vA, vB );\n\t\t\t\tcb.cross( ab );\n\n\t\t\t\tcb.normalize();\n\n\t\t\t\tface.normal.copy( cb );\n\n\t\t\t}\n\n\t\t},\n\n\t\tcomputeVertexNormals: function ( areaWeighted ) {\n\n\t\t\tif ( areaWeighted === undefined ) areaWeighted = true;\n\n\t\t\tvar v, vl, f, fl, face, vertices;\n\n\t\t\tvertices = new Array( this.vertices.length );\n\n\t\t\tfor ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {\n\n\t\t\t\tvertices[ v ] = new Vector3();\n\n\t\t\t}\n\n\t\t\tif ( areaWeighted ) {\n\n\t\t\t\t// vertex normals weighted by triangle areas\n\t\t\t\t// http://www.iquilezles.org/www/articles/normals/normals.htm\n\n\t\t\t\tvar vA, vB, vC;\n\t\t\t\tvar cb = new Vector3(), ab = new Vector3();\n\n\t\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\t\tface = this.faces[ f ];\n\n\t\t\t\t\tvA = this.vertices[ face.a ];\n\t\t\t\t\tvB = this.vertices[ face.b ];\n\t\t\t\t\tvC = this.vertices[ face.c ];\n\n\t\t\t\t\tcb.subVectors( vC, vB );\n\t\t\t\t\tab.subVectors( vA, vB );\n\t\t\t\t\tcb.cross( ab );\n\n\t\t\t\t\tvertices[ face.a ].add( cb );\n\t\t\t\t\tvertices[ face.b ].add( cb );\n\t\t\t\t\tvertices[ face.c ].add( cb );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tthis.computeFaceNormals();\n\n\t\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\t\tface = this.faces[ f ];\n\n\t\t\t\t\tvertices[ face.a ].add( face.normal );\n\t\t\t\t\tvertices[ face.b ].add( face.normal );\n\t\t\t\t\tvertices[ face.c ].add( face.normal );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfor ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {\n\n\t\t\t\tvertices[ v ].normalize();\n\n\t\t\t}\n\n\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\tface = this.faces[ f ];\n\n\t\t\t\tvar vertexNormals = face.vertexNormals;\n\n\t\t\t\tif ( vertexNormals.length === 3 ) {\n\n\t\t\t\t\tvertexNormals[ 0 ].copy( vertices[ face.a ] );\n\t\t\t\t\tvertexNormals[ 1 ].copy( vertices[ face.b ] );\n\t\t\t\t\tvertexNormals[ 2 ].copy( vertices[ face.c ] );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tvertexNormals[ 0 ] = vertices[ face.a ].clone();\n\t\t\t\t\tvertexNormals[ 1 ] = vertices[ face.b ].clone();\n\t\t\t\t\tvertexNormals[ 2 ] = vertices[ face.c ].clone();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( this.faces.length > 0 ) {\n\n\t\t\t\tthis.normalsNeedUpdate = true;\n\n\t\t\t}\n\n\t\t},\n\n\t\tcomputeFlatVertexNormals: function () {\n\n\t\t\tvar f, fl, face;\n\n\t\t\tthis.computeFaceNormals();\n\n\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\tface = this.faces[ f ];\n\n\t\t\t\tvar vertexNormals = face.vertexNormals;\n\n\t\t\t\tif ( vertexNormals.length === 3 ) {\n\n\t\t\t\t\tvertexNormals[ 0 ].copy( face.normal );\n\t\t\t\t\tvertexNormals[ 1 ].copy( face.normal );\n\t\t\t\t\tvertexNormals[ 2 ].copy( face.normal );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tvertexNormals[ 0 ] = face.normal.clone();\n\t\t\t\t\tvertexNormals[ 1 ] = face.normal.clone();\n\t\t\t\t\tvertexNormals[ 2 ] = face.normal.clone();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( this.faces.length > 0 ) {\n\n\t\t\t\tthis.normalsNeedUpdate = true;\n\n\t\t\t}\n\n\t\t},\n\n\t\tcomputeMorphNormals: function () {\n\n\t\t\tvar i, il, f, fl, face;\n\n\t\t\t// save original normals\n\t\t\t// - create temp variables on first access\n\t\t\t// otherwise just copy (for faster repeated calls)\n\n\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\tface = this.faces[ f ];\n\n\t\t\t\tif ( ! face.__originalFaceNormal ) {\n\n\t\t\t\t\tface.__originalFaceNormal = face.normal.clone();\n\n\t\t\t\t} else {\n\n\t\t\t\t\tface.__originalFaceNormal.copy( face.normal );\n\n\t\t\t\t}\n\n\t\t\t\tif ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];\n\n\t\t\t\tfor ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {\n\n\t\t\t\t\tif ( ! face.__originalVertexNormals[ i ] ) {\n\n\t\t\t\t\t\tface.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tface.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// use temp geometry to compute face and vertex normals for each morph\n\n\t\t\tvar tmpGeo = new Geometry();\n\t\t\ttmpGeo.faces = this.faces;\n\n\t\t\tfor ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {\n\n\t\t\t\t// create on first access\n\n\t\t\t\tif ( ! this.morphNormals[ i ] ) {\n\n\t\t\t\t\tthis.morphNormals[ i ] = {};\n\t\t\t\t\tthis.morphNormals[ i ].faceNormals = [];\n\t\t\t\t\tthis.morphNormals[ i ].vertexNormals = [];\n\n\t\t\t\t\tvar dstNormalsFace = this.morphNormals[ i ].faceNormals;\n\t\t\t\t\tvar dstNormalsVertex = this.morphNormals[ i ].vertexNormals;\n\n\t\t\t\t\tvar faceNormal, vertexNormals;\n\n\t\t\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\t\t\tfaceNormal = new Vector3();\n\t\t\t\t\t\tvertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() };\n\n\t\t\t\t\t\tdstNormalsFace.push( faceNormal );\n\t\t\t\t\t\tdstNormalsVertex.push( vertexNormals );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tvar morphNormals = this.morphNormals[ i ];\n\n\t\t\t\t// set vertices to morph target\n\n\t\t\t\ttmpGeo.vertices = this.morphTargets[ i ].vertices;\n\n\t\t\t\t// compute morph normals\n\n\t\t\t\ttmpGeo.computeFaceNormals();\n\t\t\t\ttmpGeo.computeVertexNormals();\n\n\t\t\t\t// store morph normals\n\n\t\t\t\tvar faceNormal, vertexNormals;\n\n\t\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\t\tface = this.faces[ f ];\n\n\t\t\t\t\tfaceNormal = morphNormals.faceNormals[ f ];\n\t\t\t\t\tvertexNormals = morphNormals.vertexNormals[ f ];\n\n\t\t\t\t\tfaceNormal.copy( face.normal );\n\n\t\t\t\t\tvertexNormals.a.copy( face.vertexNormals[ 0 ] );\n\t\t\t\t\tvertexNormals.b.copy( face.vertexNormals[ 1 ] );\n\t\t\t\t\tvertexNormals.c.copy( face.vertexNormals[ 2 ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// restore original normals\n\n\t\t\tfor ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n\t\t\t\tface = this.faces[ f ];\n\n\t\t\t\tface.normal = face.__originalFaceNormal;\n\t\t\t\tface.vertexNormals = face.__originalVertexNormals;\n\n\t\t\t}\n\n\t\t},\n\n\t\tcomputeTangents: function () {\n\n\t\t\tconsole.warn( 'THREE.Geometry: .computeTangents() has been removed.' );\n\n\t\t},\n\n\t\tcomputeLineDistances: function () {\n\n\t\t\tvar d = 0;\n\t\t\tvar vertices = this.vertices;\n\n\t\t\tfor ( var i = 0, il = vertices.length; i < il; i ++ ) {\n\n\t\t\t\tif ( i > 0 ) {\n\n\t\t\t\t\td += vertices[ i ].distanceTo( vertices[ i - 1 ] );\n\n\t\t\t\t}\n\n\t\t\t\tthis.lineDistances[ i ] = d;\n\n\t\t\t}\n\n\t\t},\n\n\t\tcomputeBoundingBox: function () {\n\n\t\t\tif ( this.boundingBox === null ) {\n\n\t\t\t\tthis.boundingBox = new Box3();\n\n\t\t\t}\n\n\t\t\tthis.boundingBox.setFromPoints( this.vertices );\n\n\t\t},\n\n\t\tcomputeBoundingSphere: function () {\n\n\t\t\tif ( this.boundingSphere === null ) {\n\n\t\t\t\tthis.boundingSphere = new Sphere();\n\n\t\t\t}\n\n\t\t\tthis.boundingSphere.setFromPoints( this.vertices );\n\n\t\t},\n\n\t\tmerge: function ( geometry, matrix, materialIndexOffset ) {\n\n\t\t\tif ( (geometry && geometry.isGeometry) === false ) {\n\n\t\t\t\tconsole.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tvar normalMatrix,\n\t\t\tvertexOffset = this.vertices.length,\n\t\t\tvertices1 = this.vertices,\n\t\t\tvertices2 = geometry.vertices,\n\t\t\tfaces1 = this.faces,\n\t\t\tfaces2 = geometry.faces,\n\t\t\tuvs1 = this.faceVertexUvs[ 0 ],\n\t\t\tuvs2 = geometry.faceVertexUvs[ 0 ],\n\t\t\tcolors1 = this.colors,\n\t\t\tcolors2 = geometry.colors;\n\n\t\t\tif ( materialIndexOffset === undefined ) materialIndexOffset = 0;\n\n\t\t\tif ( matrix !== undefined ) {\n\n\t\t\t\tnormalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n\t\t\t}\n\n\t\t\t// vertices\n\n\t\t\tfor ( var i = 0, il = vertices2.length; i < il; i ++ ) {\n\n\t\t\t\tvar vertex = vertices2[ i ];\n\n\t\t\t\tvar vertexCopy = vertex.clone();\n\n\t\t\t\tif ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );\n\n\t\t\t\tvertices1.push( vertexCopy );\n\n\t\t\t}\n\n\t\t\t// colors\n\n\t\t\tfor ( var i = 0, il = colors2.length; i < il; i ++ ) {\n\n\t\t\t\tcolors1.push( colors2[ i ].clone() );\n\n\t\t\t}\n\n\t\t\t// faces\n\n\t\t\tfor ( i = 0, il = faces2.length; i < il; i ++ ) {\n\n\t\t\t\tvar face = faces2[ i ], faceCopy, normal, color,\n\t\t\t\tfaceVertexNormals = face.vertexNormals,\n\t\t\t\tfaceVertexColors = face.vertexColors;\n\n\t\t\t\tfaceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );\n\t\t\t\tfaceCopy.normal.copy( face.normal );\n\n\t\t\t\tif ( normalMatrix !== undefined ) {\n\n\t\t\t\t\tfaceCopy.normal.applyMatrix3( normalMatrix ).normalize();\n\n\t\t\t\t}\n\n\t\t\t\tfor ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {\n\n\t\t\t\t\tnormal = faceVertexNormals[ j ].clone();\n\n\t\t\t\t\tif ( normalMatrix !== undefined ) {\n\n\t\t\t\t\t\tnormal.applyMatrix3( normalMatrix ).normalize();\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfaceCopy.vertexNormals.push( normal );\n\n\t\t\t\t}\n\n\t\t\t\tfaceCopy.color.copy( face.color );\n\n\t\t\t\tfor ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {\n\n\t\t\t\t\tcolor = faceVertexColors[ j ];\n\t\t\t\t\tfaceCopy.vertexColors.push( color.clone() );\n\n\t\t\t\t}\n\n\t\t\t\tfaceCopy.materialIndex = face.materialIndex + materialIndexOffset;\n\n\t\t\t\tfaces1.push( faceCopy );\n\n\t\t\t}\n\n\t\t\t// uvs\n\n\t\t\tfor ( i = 0, il = uvs2.length; i < il; i ++ ) {\n\n\t\t\t\tvar uv = uvs2[ i ], uvCopy = [];\n\n\t\t\t\tif ( uv === undefined ) {\n\n\t\t\t\t\tcontinue;\n\n\t\t\t\t}\n\n\t\t\t\tfor ( var j = 0, jl = uv.length; j < jl; j ++ ) {\n\n\t\t\t\t\tuvCopy.push( uv[ j ].clone() );\n\n\t\t\t\t}\n\n\t\t\t\tuvs1.push( uvCopy );\n\n\t\t\t}\n\n\t\t},\n\n\t\tmergeMesh: function ( mesh ) {\n\n\t\t\tif ( (mesh && mesh.isMesh) === false ) {\n\n\t\t\t\tconsole.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tmesh.matrixAutoUpdate && mesh.updateMatrix();\n\n\t\t\tthis.merge( mesh.geometry, mesh.matrix );\n\n\t\t},\n\n\t\t/*\n\t\t * Checks for duplicate vertices with hashmap.\n\t\t * Duplicated vertices are removed\n\t\t * and faces' vertices are updated.\n\t\t */\n\n\t\tmergeVertices: function () {\n\n\t\t\tvar verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)\n\t\t\tvar unique = [], changes = [];\n\n\t\t\tvar v, key;\n\t\t\tvar precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001\n\t\t\tvar precision = Math.pow( 10, precisionPoints );\n\t\t\tvar i, il, face;\n\t\t\tvar indices, j, jl;\n\n\t\t\tfor ( i = 0, il = this.vertices.length; i < il; i ++ ) {\n\n\t\t\t\tv = this.vertices[ i ];\n\t\t\t\tkey = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );\n\n\t\t\t\tif ( verticesMap[ key ] === undefined ) {\n\n\t\t\t\t\tverticesMap[ key ] = i;\n\t\t\t\t\tunique.push( this.vertices[ i ] );\n\t\t\t\t\tchanges[ i ] = unique.length - 1;\n\n\t\t\t\t} else {\n\n\t\t\t\t\t//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);\n\t\t\t\t\tchanges[ i ] = changes[ verticesMap[ key ] ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\n\t\t\t// if faces are completely degenerate after merging vertices, we\n\t\t\t// have to remove them from the geometry.\n\t\t\tvar faceIndicesToRemove = [];\n\n\t\t\tfor ( i = 0, il = this.faces.length; i < il; i ++ ) {\n\n\t\t\t\tface = this.faces[ i ];\n\n\t\t\t\tface.a = changes[ face.a ];\n\t\t\t\tface.b = changes[ face.b ];\n\t\t\t\tface.c = changes[ face.c ];\n\n\t\t\t\tindices = [ face.a, face.b, face.c ];\n\n\t\t\t\tvar dupIndex = - 1;\n\n\t\t\t\t// if any duplicate vertices are found in a Face3\n\t\t\t\t// we have to remove the face as nothing can be saved\n\t\t\t\tfor ( var n = 0; n < 3; n ++ ) {\n\n\t\t\t\t\tif ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {\n\n\t\t\t\t\t\tdupIndex = n;\n\t\t\t\t\t\tfaceIndicesToRemove.push( i );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfor ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {\n\n\t\t\t\tvar idx = faceIndicesToRemove[ i ];\n\n\t\t\t\tthis.faces.splice( idx, 1 );\n\n\t\t\t\tfor ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {\n\n\t\t\t\t\tthis.faceVertexUvs[ j ].splice( idx, 1 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Use unique set of vertices\n\n\t\t\tvar diff = this.vertices.length - unique.length;\n\t\t\tthis.vertices = unique;\n\t\t\treturn diff;\n\n\t\t},\n\n\t\tsortFacesByMaterialIndex: function () {\n\n\t\t\tvar faces = this.faces;\n\t\t\tvar length = faces.length;\n\n\t\t\t// tag faces\n\n\t\t\tfor ( var i = 0; i < length; i ++ ) {\n\n\t\t\t\tfaces[ i ]._id = i;\n\n\t\t\t}\n\n\t\t\t// sort faces\n\n\t\t\tfunction materialIndexSort( a, b ) {\n\n\t\t\t\treturn a.materialIndex - b.materialIndex;\n\n\t\t\t}\n\n\t\t\tfaces.sort( materialIndexSort );\n\n\t\t\t// sort uvs\n\n\t\t\tvar uvs1 = this.faceVertexUvs[ 0 ];\n\t\t\tvar uvs2 = this.faceVertexUvs[ 1 ];\n\n\t\t\tvar newUvs1, newUvs2;\n\n\t\t\tif ( uvs1 && uvs1.length === length ) newUvs1 = [];\n\t\t\tif ( uvs2 && uvs2.length === length ) newUvs2 = [];\n\n\t\t\tfor ( var i = 0; i < length; i ++ ) {\n\n\t\t\t\tvar id = faces[ i ]._id;\n\n\t\t\t\tif ( newUvs1 ) newUvs1.push( uvs1[ id ] );\n\t\t\t\tif ( newUvs2 ) newUvs2.push( uvs2[ id ] );\n\n\t\t\t}\n\n\t\t\tif ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;\n\t\t\tif ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;\n\n\t\t},\n\n\t\ttoJSON: function () {\n\n\t\t\tvar data = {\n\t\t\t\tmetadata: {\n\t\t\t\t\tversion: 4.4,\n\t\t\t\t\ttype: 'Geometry',\n\t\t\t\t\tgenerator: 'Geometry.toJSON'\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// standard Geometry serialization\n\n\t\t\tdata.uuid = this.uuid;\n\t\t\tdata.type = this.type;\n\t\t\tif ( this.name !== '' ) data.name = this.name;\n\n\t\t\tif ( this.parameters !== undefined ) {\n\n\t\t\t\tvar parameters = this.parameters;\n\n\t\t\t\tfor ( var key in parameters ) {\n\n\t\t\t\t\tif ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n\t\t\t\t}\n\n\t\t\t\treturn data;\n\n\t\t\t}\n\n\t\t\tvar vertices = [];\n\n\t\t\tfor ( var i = 0; i < this.vertices.length; i ++ ) {\n\n\t\t\t\tvar vertex = this.vertices[ i ];\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t}\n\n\t\t\tvar faces = [];\n\t\t\tvar normals = [];\n\t\t\tvar normalsHash = {};\n\t\t\tvar colors = [];\n\t\t\tvar colorsHash = {};\n\t\t\tvar uvs = [];\n\t\t\tvar uvsHash = {};\n\n\t\t\tfor ( var i = 0; i < this.faces.length; i ++ ) {\n\n\t\t\t\tvar face = this.faces[ i ];\n\n\t\t\t\tvar hasMaterial = true;\n\t\t\t\tvar hasFaceUv = false; // deprecated\n\t\t\t\tvar hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;\n\t\t\t\tvar hasFaceNormal = face.normal.length() > 0;\n\t\t\t\tvar hasFaceVertexNormal = face.vertexNormals.length > 0;\n\t\t\t\tvar hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;\n\t\t\t\tvar hasFaceVertexColor = face.vertexColors.length > 0;\n\n\t\t\t\tvar faceType = 0;\n\n\t\t\t\tfaceType = setBit( faceType, 0, 0 ); // isQuad\n\t\t\t\tfaceType = setBit( faceType, 1, hasMaterial );\n\t\t\t\tfaceType = setBit( faceType, 2, hasFaceUv );\n\t\t\t\tfaceType = setBit( faceType, 3, hasFaceVertexUv );\n\t\t\t\tfaceType = setBit( faceType, 4, hasFaceNormal );\n\t\t\t\tfaceType = setBit( faceType, 5, hasFaceVertexNormal );\n\t\t\t\tfaceType = setBit( faceType, 6, hasFaceColor );\n\t\t\t\tfaceType = setBit( faceType, 7, hasFaceVertexColor );\n\n\t\t\t\tfaces.push( faceType );\n\t\t\t\tfaces.push( face.a, face.b, face.c );\n\t\t\t\tfaces.push( face.materialIndex );\n\n\t\t\t\tif ( hasFaceVertexUv ) {\n\n\t\t\t\t\tvar faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];\n\n\t\t\t\t\tfaces.push(\n\t\t\t\t\t\tgetUvIndex( faceVertexUvs[ 0 ] ),\n\t\t\t\t\t\tgetUvIndex( faceVertexUvs[ 1 ] ),\n\t\t\t\t\t\tgetUvIndex( faceVertexUvs[ 2 ] )\n\t\t\t\t\t);\n\n\t\t\t\t}\n\n\t\t\t\tif ( hasFaceNormal ) {\n\n\t\t\t\t\tfaces.push( getNormalIndex( face.normal ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( hasFaceVertexNormal ) {\n\n\t\t\t\t\tvar vertexNormals = face.vertexNormals;\n\n\t\t\t\t\tfaces.push(\n\t\t\t\t\t\tgetNormalIndex( vertexNormals[ 0 ] ),\n\t\t\t\t\t\tgetNormalIndex( vertexNormals[ 1 ] ),\n\t\t\t\t\t\tgetNormalIndex( vertexNormals[ 2 ] )\n\t\t\t\t\t);\n\n\t\t\t\t}\n\n\t\t\t\tif ( hasFaceColor ) {\n\n\t\t\t\t\tfaces.push( getColorIndex( face.color ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( hasFaceVertexColor ) {\n\n\t\t\t\t\tvar vertexColors = face.vertexColors;\n\n\t\t\t\t\tfaces.push(\n\t\t\t\t\t\tgetColorIndex( vertexColors[ 0 ] ),\n\t\t\t\t\t\tgetColorIndex( vertexColors[ 1 ] ),\n\t\t\t\t\t\tgetColorIndex( vertexColors[ 2 ] )\n\t\t\t\t\t);\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction setBit( value, position, enabled ) {\n\n\t\t\t\treturn enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );\n\n\t\t\t}\n\n\t\t\tfunction getNormalIndex( normal ) {\n\n\t\t\t\tvar hash = normal.x.toString() + normal.y.toString() + normal.z.toString();\n\n\t\t\t\tif ( normalsHash[ hash ] !== undefined ) {\n\n\t\t\t\t\treturn normalsHash[ hash ];\n\n\t\t\t\t}\n\n\t\t\t\tnormalsHash[ hash ] = normals.length / 3;\n\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\treturn normalsHash[ hash ];\n\n\t\t\t}\n\n\t\t\tfunction getColorIndex( color ) {\n\n\t\t\t\tvar hash = color.r.toString() + color.g.toString() + color.b.toString();\n\n\t\t\t\tif ( colorsHash[ hash ] !== undefined ) {\n\n\t\t\t\t\treturn colorsHash[ hash ];\n\n\t\t\t\t}\n\n\t\t\t\tcolorsHash[ hash ] = colors.length;\n\t\t\t\tcolors.push( color.getHex() );\n\n\t\t\t\treturn colorsHash[ hash ];\n\n\t\t\t}\n\n\t\t\tfunction getUvIndex( uv ) {\n\n\t\t\t\tvar hash = uv.x.toString() + uv.y.toString();\n\n\t\t\t\tif ( uvsHash[ hash ] !== undefined ) {\n\n\t\t\t\t\treturn uvsHash[ hash ];\n\n\t\t\t\t}\n\n\t\t\t\tuvsHash[ hash ] = uvs.length / 2;\n\t\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t\t\treturn uvsHash[ hash ];\n\n\t\t\t}\n\n\t\t\tdata.data = {};\n\n\t\t\tdata.data.vertices = vertices;\n\t\t\tdata.data.normals = normals;\n\t\t\tif ( colors.length > 0 ) data.data.colors = colors;\n\t\t\tif ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility\n\t\t\tdata.data.faces = faces;\n\n\t\t\treturn data;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\t/*\n\t\t\t// Handle primitives\n\n\t\t\tvar parameters = this.parameters;\n\n\t\t\tif ( parameters !== undefined ) {\n\n\t\t\t\tvar values = [];\n\n\t\t\t\tfor ( var key in parameters ) {\n\n\t\t\t\t\tvalues.push( parameters[ key ] );\n\n\t\t\t\t}\n\n\t\t\t\tvar geometry = Object.create( this.constructor.prototype );\n\t\t\t\tthis.constructor.apply( geometry, values );\n\t\t\t\treturn geometry;\n\n\t\t\t}\n\n\t\t\treturn new this.constructor().copy( this );\n\t\t\t*/\n\n\t\t\treturn new Geometry().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tthis.vertices = [];\n\t\t\tthis.faces = [];\n\t\t\tthis.faceVertexUvs = [ [] ];\n\t\t\tthis.colors = [];\n\n\t\t\tvar vertices = source.vertices;\n\n\t\t\tfor ( var i = 0, il = vertices.length; i < il; i ++ ) {\n\n\t\t\t\tthis.vertices.push( vertices[ i ].clone() );\n\n\t\t\t}\n\n\t\t\tvar colors = source.colors;\n\n\t\t\tfor ( var i = 0, il = colors.length; i < il; i ++ ) {\n\n\t\t\t\tthis.colors.push( colors[ i ].clone() );\n\n\t\t\t}\n\n\t\t\tvar faces = source.faces;\n\n\t\t\tfor ( var i = 0, il = faces.length; i < il; i ++ ) {\n\n\t\t\t\tthis.faces.push( faces[ i ].clone() );\n\n\t\t\t}\n\n\t\t\tfor ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {\n\n\t\t\t\tvar faceVertexUvs = source.faceVertexUvs[ i ];\n\n\t\t\t\tif ( this.faceVertexUvs[ i ] === undefined ) {\n\n\t\t\t\t\tthis.faceVertexUvs[ i ] = [];\n\n\t\t\t\t}\n\n\t\t\t\tfor ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {\n\n\t\t\t\t\tvar uvs = faceVertexUvs[ j ], uvsCopy = [];\n\n\t\t\t\t\tfor ( var k = 0, kl = uvs.length; k < kl; k ++ ) {\n\n\t\t\t\t\t\tvar uv = uvs[ k ];\n\n\t\t\t\t\t\tuvsCopy.push( uv.clone() );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.faceVertexUvs[ i ].push( uvsCopy );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdispose: function () {\n\n\t\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t\t}\n\n\t} );\n\n\tvar count$3 = 0;\n\tfunction GeometryIdCount() { return count$3++; }\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction DirectGeometry() {\n\n\t\tObject.defineProperty( this, 'id', { value: GeometryIdCount() } );\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'DirectGeometry';\n\n\t\tthis.indices = [];\n\t\tthis.vertices = [];\n\t\tthis.normals = [];\n\t\tthis.colors = [];\n\t\tthis.uvs = [];\n\t\tthis.uvs2 = [];\n\n\t\tthis.groups = [];\n\n\t\tthis.morphTargets = {};\n\n\t\tthis.skinWeights = [];\n\t\tthis.skinIndices = [];\n\n\t\t// this.lineDistances = [];\n\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\t// update flags\n\n\t\tthis.verticesNeedUpdate = false;\n\t\tthis.normalsNeedUpdate = false;\n\t\tthis.colorsNeedUpdate = false;\n\t\tthis.uvsNeedUpdate = false;\n\t\tthis.groupsNeedUpdate = false;\n\n\t}\n\n\tObject.assign( DirectGeometry.prototype, EventDispatcher.prototype, {\n\n\t\tcomputeBoundingBox: Geometry.prototype.computeBoundingBox,\n\t\tcomputeBoundingSphere: Geometry.prototype.computeBoundingSphere,\n\n\t\tcomputeFaceNormals: function () {\n\n\t\t\tconsole.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' );\n\n\t\t},\n\n\t\tcomputeVertexNormals: function () {\n\n\t\t\tconsole.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' );\n\n\t\t},\n\n\t\tcomputeGroups: function ( geometry ) {\n\n\t\t\tvar group;\n\t\t\tvar groups = [];\n\t\t\tvar materialIndex;\n\n\t\t\tvar faces = geometry.faces;\n\n\t\t\tfor ( var i = 0; i < faces.length; i ++ ) {\n\n\t\t\t\tvar face = faces[ i ];\n\n\t\t\t\t// materials\n\n\t\t\t\tif ( face.materialIndex !== materialIndex ) {\n\n\t\t\t\t\tmaterialIndex = face.materialIndex;\n\n\t\t\t\t\tif ( group !== undefined ) {\n\n\t\t\t\t\t\tgroup.count = ( i * 3 ) - group.start;\n\t\t\t\t\t\tgroups.push( group );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tgroup = {\n\t\t\t\t\t\tstart: i * 3,\n\t\t\t\t\t\tmaterialIndex: materialIndex\n\t\t\t\t\t};\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( group !== undefined ) {\n\n\t\t\t\tgroup.count = ( i * 3 ) - group.start;\n\t\t\t\tgroups.push( group );\n\n\t\t\t}\n\n\t\t\tthis.groups = groups;\n\n\t\t},\n\n\t\tfromGeometry: function ( geometry ) {\n\n\t\t\tvar faces = geometry.faces;\n\t\t\tvar vertices = geometry.vertices;\n\t\t\tvar faceVertexUvs = geometry.faceVertexUvs;\n\n\t\t\tvar hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;\n\t\t\tvar hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;\n\n\t\t\t// morphs\n\n\t\t\tvar morphTargets = geometry.morphTargets;\n\t\t\tvar morphTargetsLength = morphTargets.length;\n\n\t\t\tvar morphTargetsPosition;\n\n\t\t\tif ( morphTargetsLength > 0 ) {\n\n\t\t\t\tmorphTargetsPosition = [];\n\n\t\t\t\tfor ( var i = 0; i < morphTargetsLength; i ++ ) {\n\n\t\t\t\t\tmorphTargetsPosition[ i ] = [];\n\n\t\t\t\t}\n\n\t\t\t\tthis.morphTargets.position = morphTargetsPosition;\n\n\t\t\t}\n\n\t\t\tvar morphNormals = geometry.morphNormals;\n\t\t\tvar morphNormalsLength = morphNormals.length;\n\n\t\t\tvar morphTargetsNormal;\n\n\t\t\tif ( morphNormalsLength > 0 ) {\n\n\t\t\t\tmorphTargetsNormal = [];\n\n\t\t\t\tfor ( var i = 0; i < morphNormalsLength; i ++ ) {\n\n\t\t\t\t\tmorphTargetsNormal[ i ] = [];\n\n\t\t\t\t}\n\n\t\t\t\tthis.morphTargets.normal = morphTargetsNormal;\n\n\t\t\t}\n\n\t\t\t// skins\n\n\t\t\tvar skinIndices = geometry.skinIndices;\n\t\t\tvar skinWeights = geometry.skinWeights;\n\n\t\t\tvar hasSkinIndices = skinIndices.length === vertices.length;\n\t\t\tvar hasSkinWeights = skinWeights.length === vertices.length;\n\n\t\t\t//\n\n\t\t\tfor ( var i = 0; i < faces.length; i ++ ) {\n\n\t\t\t\tvar face = faces[ i ];\n\n\t\t\t\tthis.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );\n\n\t\t\t\tvar vertexNormals = face.vertexNormals;\n\n\t\t\t\tif ( vertexNormals.length === 3 ) {\n\n\t\t\t\t\tthis.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tvar normal = face.normal;\n\n\t\t\t\t\tthis.normals.push( normal, normal, normal );\n\n\t\t\t\t}\n\n\t\t\t\tvar vertexColors = face.vertexColors;\n\n\t\t\t\tif ( vertexColors.length === 3 ) {\n\n\t\t\t\t\tthis.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tvar color = face.color;\n\n\t\t\t\t\tthis.colors.push( color, color, color );\n\n\t\t\t\t}\n\n\t\t\t\tif ( hasFaceVertexUv === true ) {\n\n\t\t\t\t\tvar vertexUvs = faceVertexUvs[ 0 ][ i ];\n\n\t\t\t\t\tif ( vertexUvs !== undefined ) {\n\n\t\t\t\t\t\tthis.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );\n\n\t\t\t\t\t\tthis.uvs.push( new Vector2(), new Vector2(), new Vector2() );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( hasFaceVertexUv2 === true ) {\n\n\t\t\t\t\tvar vertexUvs = faceVertexUvs[ 1 ][ i ];\n\n\t\t\t\t\tif ( vertexUvs !== undefined ) {\n\n\t\t\t\t\t\tthis.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );\n\n\t\t\t\t\t\tthis.uvs2.push( new Vector2(), new Vector2(), new Vector2() );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// morphs\n\n\t\t\t\tfor ( var j = 0; j < morphTargetsLength; j ++ ) {\n\n\t\t\t\t\tvar morphTarget = morphTargets[ j ].vertices;\n\n\t\t\t\t\tmorphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );\n\n\t\t\t\t}\n\n\t\t\t\tfor ( var j = 0; j < morphNormalsLength; j ++ ) {\n\n\t\t\t\t\tvar morphNormal = morphNormals[ j ].vertexNormals[ i ];\n\n\t\t\t\t\tmorphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );\n\n\t\t\t\t}\n\n\t\t\t\t// skins\n\n\t\t\t\tif ( hasSkinIndices ) {\n\n\t\t\t\t\tthis.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( hasSkinWeights ) {\n\n\t\t\t\t\tthis.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.computeGroups( geometry );\n\n\t\t\tthis.verticesNeedUpdate = geometry.verticesNeedUpdate;\n\t\t\tthis.normalsNeedUpdate = geometry.normalsNeedUpdate;\n\t\t\tthis.colorsNeedUpdate = geometry.colorsNeedUpdate;\n\t\t\tthis.uvsNeedUpdate = geometry.uvsNeedUpdate;\n\t\t\tthis.groupsNeedUpdate = geometry.groupsNeedUpdate;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdispose: function () {\n\n\t\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction BufferGeometry() {\n\n\t\tObject.defineProperty( this, 'id', { value: GeometryIdCount() } );\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'BufferGeometry';\n\n\t\tthis.index = null;\n\t\tthis.attributes = {};\n\n\t\tthis.morphAttributes = {};\n\n\t\tthis.groups = [];\n\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\tthis.drawRange = { start: 0, count: Infinity };\n\n\t}\n\n\tObject.assign( BufferGeometry.prototype, EventDispatcher.prototype, {\n\n\t\tisBufferGeometry: true,\n\n\t\tgetIndex: function () {\n\n\t\t\treturn this.index;\n\n\t\t},\n\n\t\tsetIndex: function ( index ) {\n\n\t\t\tthis.index = index;\n\n\t\t},\n\n\t\taddAttribute: function ( name, attribute ) {\n\n\t\t\tif ( (attribute && attribute.isBufferAttribute) === false && (attribute && attribute.isInterleavedBufferAttribute) === false ) {\n\n\t\t\t\tconsole.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );\n\n\t\t\t\tthis.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tif ( name === 'index' ) {\n\n\t\t\t\tconsole.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );\n\t\t\t\tthis.setIndex( attribute );\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tthis.attributes[ name ] = attribute;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tgetAttribute: function ( name ) {\n\n\t\t\treturn this.attributes[ name ];\n\n\t\t},\n\n\t\tremoveAttribute: function ( name ) {\n\n\t\t\tdelete this.attributes[ name ];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddGroup: function ( start, count, materialIndex ) {\n\n\t\t\tthis.groups.push( {\n\n\t\t\t\tstart: start,\n\t\t\t\tcount: count,\n\t\t\t\tmaterialIndex: materialIndex !== undefined ? materialIndex : 0\n\n\t\t\t} );\n\n\t\t},\n\n\t\tclearGroups: function () {\n\n\t\t\tthis.groups = [];\n\n\t\t},\n\n\t\tsetDrawRange: function ( start, count ) {\n\n\t\t\tthis.drawRange.start = start;\n\t\t\tthis.drawRange.count = count;\n\n\t\t},\n\n\t\tapplyMatrix: function ( matrix ) {\n\n\t\t\tvar position = this.attributes.position;\n\n\t\t\tif ( position !== undefined ) {\n\n\t\t\t\tmatrix.applyToVector3Array( position.array );\n\t\t\t\tposition.needsUpdate = true;\n\n\t\t\t}\n\n\t\t\tvar normal = this.attributes.normal;\n\n\t\t\tif ( normal !== undefined ) {\n\n\t\t\t\tvar normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n\t\t\t\tnormalMatrix.applyToVector3Array( normal.array );\n\t\t\t\tnormal.needsUpdate = true;\n\n\t\t\t}\n\n\t\t\tif ( this.boundingBox !== null ) {\n\n\t\t\t\tthis.computeBoundingBox();\n\n\t\t\t}\n\n\t\t\tif ( this.boundingSphere !== null ) {\n\n\t\t\t\tthis.computeBoundingSphere();\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\trotateX: function () {\n\n\t\t\t// rotate geometry around world x-axis\n\n\t\t\tvar m1;\n\n\t\t\treturn function rotateX( angle ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeRotationX( angle );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\trotateY: function () {\n\n\t\t\t// rotate geometry around world y-axis\n\n\t\t\tvar m1;\n\n\t\t\treturn function rotateY( angle ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeRotationY( angle );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\trotateZ: function () {\n\n\t\t\t// rotate geometry around world z-axis\n\n\t\t\tvar m1;\n\n\t\t\treturn function rotateZ( angle ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeRotationZ( angle );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttranslate: function () {\n\n\t\t\t// translate geometry\n\n\t\t\tvar m1;\n\n\t\t\treturn function translate( x, y, z ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeTranslation( x, y, z );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tscale: function () {\n\n\t\t\t// scale geometry\n\n\t\t\tvar m1;\n\n\t\t\treturn function scale( x, y, z ) {\n\n\t\t\t\tif ( m1 === undefined ) m1 = new Matrix4();\n\n\t\t\t\tm1.makeScale( x, y, z );\n\n\t\t\t\tthis.applyMatrix( m1 );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tlookAt: function () {\n\n\t\t\tvar obj;\n\n\t\t\treturn function lookAt( vector ) {\n\n\t\t\t\tif ( obj === undefined ) obj = new Object3D();\n\n\t\t\t\tobj.lookAt( vector );\n\n\t\t\t\tobj.updateMatrix();\n\n\t\t\t\tthis.applyMatrix( obj.matrix );\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tcenter: function () {\n\n\t\t\tthis.computeBoundingBox();\n\n\t\t\tvar offset = this.boundingBox.getCenter().negate();\n\n\t\t\tthis.translate( offset.x, offset.y, offset.z );\n\n\t\t\treturn offset;\n\n\t\t},\n\n\t\tsetFromObject: function ( object ) {\n\n\t\t\t// console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );\n\n\t\t\tvar geometry = object.geometry;\n\n\t\t\tif ( (object && object.isPoints) || (object && object.isLine) ) {\n\n\t\t\t\tvar positions = new Float32Attribute( geometry.vertices.length * 3, 3 );\n\t\t\t\tvar colors = new Float32Attribute( geometry.colors.length * 3, 3 );\n\n\t\t\t\tthis.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );\n\t\t\t\tthis.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );\n\n\t\t\t\tif ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {\n\n\t\t\t\t\tvar lineDistances = new Float32Attribute( geometry.lineDistances.length, 1 );\n\n\t\t\t\t\tthis.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( geometry.boundingSphere !== null ) {\n\n\t\t\t\t\tthis.boundingSphere = geometry.boundingSphere.clone();\n\n\t\t\t\t}\n\n\t\t\t\tif ( geometry.boundingBox !== null ) {\n\n\t\t\t\t\tthis.boundingBox = geometry.boundingBox.clone();\n\n\t\t\t\t}\n\n\t\t\t} else if ( (object && object.isMesh) ) {\n\n\t\t\t\tif ( (geometry && geometry.isGeometry) ) {\n\n\t\t\t\t\tthis.fromGeometry( geometry );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tupdateFromObject: function ( object ) {\n\n\t\t\tvar geometry = object.geometry;\n\n\t\t\tif ( (object && object.isMesh) ) {\n\n\t\t\t\tvar direct = geometry.__directGeometry;\n\n\t\t\t\tif ( geometry.elementsNeedUpdate === true ) {\n\n\t\t\t\t\tdirect = undefined;\n\t\t\t\t\tgeometry.elementsNeedUpdate = false;\n\n\t\t\t\t}\n\n\t\t\t\tif ( direct === undefined ) {\n\n\t\t\t\t\treturn this.fromGeometry( geometry );\n\n\t\t\t\t}\n\n\t\t\t\tdirect.verticesNeedUpdate = geometry.verticesNeedUpdate;\n\t\t\t\tdirect.normalsNeedUpdate = geometry.normalsNeedUpdate;\n\t\t\t\tdirect.colorsNeedUpdate = geometry.colorsNeedUpdate;\n\t\t\t\tdirect.uvsNeedUpdate = geometry.uvsNeedUpdate;\n\t\t\t\tdirect.groupsNeedUpdate = geometry.groupsNeedUpdate;\n\n\t\t\t\tgeometry.verticesNeedUpdate = false;\n\t\t\t\tgeometry.normalsNeedUpdate = false;\n\t\t\t\tgeometry.colorsNeedUpdate = false;\n\t\t\t\tgeometry.uvsNeedUpdate = false;\n\t\t\t\tgeometry.groupsNeedUpdate = false;\n\n\t\t\t\tgeometry = direct;\n\n\t\t\t}\n\n\t\t\tvar attribute;\n\n\t\t\tif ( geometry.verticesNeedUpdate === true ) {\n\n\t\t\t\tattribute = this.attributes.position;\n\n\t\t\t\tif ( attribute !== undefined ) {\n\n\t\t\t\t\tattribute.copyVector3sArray( geometry.vertices );\n\t\t\t\t\tattribute.needsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.verticesNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\tif ( geometry.normalsNeedUpdate === true ) {\n\n\t\t\t\tattribute = this.attributes.normal;\n\n\t\t\t\tif ( attribute !== undefined ) {\n\n\t\t\t\t\tattribute.copyVector3sArray( geometry.normals );\n\t\t\t\t\tattribute.needsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.normalsNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\tif ( geometry.colorsNeedUpdate === true ) {\n\n\t\t\t\tattribute = this.attributes.color;\n\n\t\t\t\tif ( attribute !== undefined ) {\n\n\t\t\t\t\tattribute.copyColorsArray( geometry.colors );\n\t\t\t\t\tattribute.needsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.colorsNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\tif ( geometry.uvsNeedUpdate ) {\n\n\t\t\t\tattribute = this.attributes.uv;\n\n\t\t\t\tif ( attribute !== undefined ) {\n\n\t\t\t\t\tattribute.copyVector2sArray( geometry.uvs );\n\t\t\t\t\tattribute.needsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.uvsNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\tif ( geometry.lineDistancesNeedUpdate ) {\n\n\t\t\t\tattribute = this.attributes.lineDistance;\n\n\t\t\t\tif ( attribute !== undefined ) {\n\n\t\t\t\t\tattribute.copyArray( geometry.lineDistances );\n\t\t\t\t\tattribute.needsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.lineDistancesNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\tif ( geometry.groupsNeedUpdate ) {\n\n\t\t\t\tgeometry.computeGroups( object.geometry );\n\t\t\t\tthis.groups = geometry.groups;\n\n\t\t\t\tgeometry.groupsNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tfromGeometry: function ( geometry ) {\n\n\t\t\tgeometry.__directGeometry = new DirectGeometry().fromGeometry( geometry );\n\n\t\t\treturn this.fromDirectGeometry( geometry.__directGeometry );\n\n\t\t},\n\n\t\tfromDirectGeometry: function ( geometry ) {\n\n\t\t\tvar positions = new Float32Array( geometry.vertices.length * 3 );\n\t\t\tthis.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );\n\n\t\t\tif ( geometry.normals.length > 0 ) {\n\n\t\t\t\tvar normals = new Float32Array( geometry.normals.length * 3 );\n\t\t\t\tthis.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );\n\n\t\t\t}\n\n\t\t\tif ( geometry.colors.length > 0 ) {\n\n\t\t\t\tvar colors = new Float32Array( geometry.colors.length * 3 );\n\t\t\t\tthis.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );\n\n\t\t\t}\n\n\t\t\tif ( geometry.uvs.length > 0 ) {\n\n\t\t\t\tvar uvs = new Float32Array( geometry.uvs.length * 2 );\n\t\t\t\tthis.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );\n\n\t\t\t}\n\n\t\t\tif ( geometry.uvs2.length > 0 ) {\n\n\t\t\t\tvar uvs2 = new Float32Array( geometry.uvs2.length * 2 );\n\t\t\t\tthis.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );\n\n\t\t\t}\n\n\t\t\tif ( geometry.indices.length > 0 ) {\n\n\t\t\t\tvar TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array;\n\t\t\t\tvar indices = new TypeArray( geometry.indices.length * 3 );\n\t\t\t\tthis.setIndex( new BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) );\n\n\t\t\t}\n\n\t\t\t// groups\n\n\t\t\tthis.groups = geometry.groups;\n\n\t\t\t// morphs\n\n\t\t\tfor ( var name in geometry.morphTargets ) {\n\n\t\t\t\tvar array = [];\n\t\t\t\tvar morphTargets = geometry.morphTargets[ name ];\n\n\t\t\t\tfor ( var i = 0, l = morphTargets.length; i < l; i ++ ) {\n\n\t\t\t\t\tvar morphTarget = morphTargets[ i ];\n\n\t\t\t\t\tvar attribute = new Float32Attribute( morphTarget.length * 3, 3 );\n\n\t\t\t\t\tarray.push( attribute.copyVector3sArray( morphTarget ) );\n\n\t\t\t\t}\n\n\t\t\t\tthis.morphAttributes[ name ] = array;\n\n\t\t\t}\n\n\t\t\t// skinning\n\n\t\t\tif ( geometry.skinIndices.length > 0 ) {\n\n\t\t\t\tvar skinIndices = new Float32Attribute( geometry.skinIndices.length * 4, 4 );\n\t\t\t\tthis.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );\n\n\t\t\t}\n\n\t\t\tif ( geometry.skinWeights.length > 0 ) {\n\n\t\t\t\tvar skinWeights = new Float32Attribute( geometry.skinWeights.length * 4, 4 );\n\t\t\t\tthis.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( geometry.boundingSphere !== null ) {\n\n\t\t\t\tthis.boundingSphere = geometry.boundingSphere.clone();\n\n\t\t\t}\n\n\t\t\tif ( geometry.boundingBox !== null ) {\n\n\t\t\t\tthis.boundingBox = geometry.boundingBox.clone();\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcomputeBoundingBox: function () {\n\n\t\t\tif ( this.boundingBox === null ) {\n\n\t\t\t\tthis.boundingBox = new Box3();\n\n\t\t\t}\n\n\t\t\tvar positions = this.attributes.position.array;\n\n\t\t\tif ( positions !== undefined ) {\n\n\t\t\t\tthis.boundingBox.setFromArray( positions );\n\n\t\t\t} else {\n\n\t\t\t\tthis.boundingBox.makeEmpty();\n\n\t\t\t}\n\n\t\t\tif ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {\n\n\t\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The \"position\" attribute is likely to have NaN values.', this );\n\n\t\t\t}\n\n\t\t},\n\n\t\tcomputeBoundingSphere: function () {\n\n\t\t\tvar box = new Box3();\n\t\t\tvar vector = new Vector3();\n\n\t\t\treturn function computeBoundingSphere() {\n\n\t\t\t\tif ( this.boundingSphere === null ) {\n\n\t\t\t\t\tthis.boundingSphere = new Sphere();\n\n\t\t\t\t}\n\n\t\t\t\tvar positions = this.attributes.position;\n\n\t\t\t\tif ( positions ) {\n\n\t\t\t\t\tvar array = positions.array;\n\t\t\t\t\tvar center = this.boundingSphere.center;\n\n\t\t\t\t\tbox.setFromArray( array );\n\t\t\t\t\tbox.getCenter( center );\n\n\t\t\t\t\t// hoping to find a boundingSphere with a radius smaller than the\n\t\t\t\t\t// boundingSphere of the boundingBox: sqrt(3) smaller in the best case\n\n\t\t\t\t\tvar maxRadiusSq = 0;\n\n\t\t\t\t\tfor ( var i = 0, il = array.length; i < il; i += 3 ) {\n\n\t\t\t\t\t\tvector.fromArray( array, i );\n\t\t\t\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.boundingSphere.radius = Math.sqrt( maxRadiusSq );\n\n\t\t\t\t\tif ( isNaN( this.boundingSphere.radius ) ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The \"position\" attribute is likely to have NaN values.', this );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tcomputeFaceNormals: function () {\n\n\t\t\t// backwards compatibility\n\n\t\t},\n\n\t\tcomputeVertexNormals: function () {\n\n\t\t\tvar index = this.index;\n\t\t\tvar attributes = this.attributes;\n\t\t\tvar groups = this.groups;\n\n\t\t\tif ( attributes.position ) {\n\n\t\t\t\tvar positions = attributes.position.array;\n\n\t\t\t\tif ( attributes.normal === undefined ) {\n\n\t\t\t\t\tthis.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// reset existing normals to zero\n\n\t\t\t\t\tvar array = attributes.normal.array;\n\n\t\t\t\t\tfor ( var i = 0, il = array.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tarray[ i ] = 0;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tvar normals = attributes.normal.array;\n\n\t\t\t\tvar vA, vB, vC,\n\n\t\t\t\tpA = new Vector3(),\n\t\t\t\tpB = new Vector3(),\n\t\t\t\tpC = new Vector3(),\n\n\t\t\t\tcb = new Vector3(),\n\t\t\t\tab = new Vector3();\n\n\t\t\t\t// indexed elements\n\n\t\t\t\tif ( index ) {\n\n\t\t\t\t\tvar indices = index.array;\n\n\t\t\t\t\tif ( groups.length === 0 ) {\n\n\t\t\t\t\t\tthis.addGroup( 0, indices.length );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( var j = 0, jl = groups.length; j < jl; ++ j ) {\n\n\t\t\t\t\t\tvar group = groups[ j ];\n\n\t\t\t\t\t\tvar start = group.start;\n\t\t\t\t\t\tvar count = group.count;\n\n\t\t\t\t\t\tfor ( var i = start, il = start + count; i < il; i += 3 ) {\n\n\t\t\t\t\t\t\tvA = indices[ i + 0 ] * 3;\n\t\t\t\t\t\t\tvB = indices[ i + 1 ] * 3;\n\t\t\t\t\t\t\tvC = indices[ i + 2 ] * 3;\n\n\t\t\t\t\t\t\tpA.fromArray( positions, vA );\n\t\t\t\t\t\t\tpB.fromArray( positions, vB );\n\t\t\t\t\t\t\tpC.fromArray( positions, vC );\n\n\t\t\t\t\t\t\tcb.subVectors( pC, pB );\n\t\t\t\t\t\t\tab.subVectors( pA, pB );\n\t\t\t\t\t\t\tcb.cross( ab );\n\n\t\t\t\t\t\t\tnormals[ vA ] += cb.x;\n\t\t\t\t\t\t\tnormals[ vA + 1 ] += cb.y;\n\t\t\t\t\t\t\tnormals[ vA + 2 ] += cb.z;\n\n\t\t\t\t\t\t\tnormals[ vB ] += cb.x;\n\t\t\t\t\t\t\tnormals[ vB + 1 ] += cb.y;\n\t\t\t\t\t\t\tnormals[ vB + 2 ] += cb.z;\n\n\t\t\t\t\t\t\tnormals[ vC ] += cb.x;\n\t\t\t\t\t\t\tnormals[ vC + 1 ] += cb.y;\n\t\t\t\t\t\t\tnormals[ vC + 2 ] += cb.z;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// non-indexed elements (unconnected triangle soup)\n\n\t\t\t\t\tfor ( var i = 0, il = positions.length; i < il; i += 9 ) {\n\n\t\t\t\t\t\tpA.fromArray( positions, i );\n\t\t\t\t\t\tpB.fromArray( positions, i + 3 );\n\t\t\t\t\t\tpC.fromArray( positions, i + 6 );\n\n\t\t\t\t\t\tcb.subVectors( pC, pB );\n\t\t\t\t\t\tab.subVectors( pA, pB );\n\t\t\t\t\t\tcb.cross( ab );\n\n\t\t\t\t\t\tnormals[ i ] = cb.x;\n\t\t\t\t\t\tnormals[ i + 1 ] = cb.y;\n\t\t\t\t\t\tnormals[ i + 2 ] = cb.z;\n\n\t\t\t\t\t\tnormals[ i + 3 ] = cb.x;\n\t\t\t\t\t\tnormals[ i + 4 ] = cb.y;\n\t\t\t\t\t\tnormals[ i + 5 ] = cb.z;\n\n\t\t\t\t\t\tnormals[ i + 6 ] = cb.x;\n\t\t\t\t\t\tnormals[ i + 7 ] = cb.y;\n\t\t\t\t\t\tnormals[ i + 8 ] = cb.z;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tthis.normalizeNormals();\n\n\t\t\t\tattributes.normal.needsUpdate = true;\n\n\t\t\t}\n\n\t\t},\n\n\t\tmerge: function ( geometry, offset ) {\n\n\t\t\tif ( (geometry && geometry.isBufferGeometry) === false ) {\n\n\t\t\t\tconsole.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tif ( offset === undefined ) offset = 0;\n\n\t\t\tvar attributes = this.attributes;\n\n\t\t\tfor ( var key in attributes ) {\n\n\t\t\t\tif ( geometry.attributes[ key ] === undefined ) continue;\n\n\t\t\t\tvar attribute1 = attributes[ key ];\n\t\t\t\tvar attributeArray1 = attribute1.array;\n\n\t\t\t\tvar attribute2 = geometry.attributes[ key ];\n\t\t\t\tvar attributeArray2 = attribute2.array;\n\n\t\t\t\tvar attributeSize = attribute2.itemSize;\n\n\t\t\t\tfor ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {\n\n\t\t\t\t\tattributeArray1[ j ] = attributeArray2[ i ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tnormalizeNormals: function () {\n\n\t\t\tvar normals = this.attributes.normal.array;\n\n\t\t\tvar x, y, z, n;\n\n\t\t\tfor ( var i = 0, il = normals.length; i < il; i += 3 ) {\n\n\t\t\t\tx = normals[ i ];\n\t\t\t\ty = normals[ i + 1 ];\n\t\t\t\tz = normals[ i + 2 ];\n\n\t\t\t\tn = 1.0 / Math.sqrt( x * x + y * y + z * z );\n\n\t\t\t\tnormals[ i ] *= n;\n\t\t\t\tnormals[ i + 1 ] *= n;\n\t\t\t\tnormals[ i + 2 ] *= n;\n\n\t\t\t}\n\n\t\t},\n\n\t\ttoNonIndexed: function () {\n\n\t\t\tif ( this.index === null ) {\n\n\t\t\t\tconsole.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' );\n\t\t\t\treturn this;\n\n\t\t\t}\n\n\t\t\tvar geometry2 = new BufferGeometry();\n\n\t\t\tvar indices = this.index.array;\n\t\t\tvar attributes = this.attributes;\n\n\t\t\tfor ( var name in attributes ) {\n\n\t\t\t\tvar attribute = attributes[ name ];\n\n\t\t\t\tvar array = attribute.array;\n\t\t\t\tvar itemSize = attribute.itemSize;\n\n\t\t\t\tvar array2 = new array.constructor( indices.length * itemSize );\n\n\t\t\t\tvar index = 0, index2 = 0;\n\n\t\t\t\tfor ( var i = 0, l = indices.length; i < l; i ++ ) {\n\n\t\t\t\t\tindex = indices[ i ] * itemSize;\n\n\t\t\t\t\tfor ( var j = 0; j < itemSize; j ++ ) {\n\n\t\t\t\t\t\tarray2[ index2 ++ ] = array[ index ++ ];\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tgeometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) );\n\n\t\t\t}\n\n\t\t\treturn geometry2;\n\n\t\t},\n\n\t\ttoJSON: function () {\n\n\t\t\tvar data = {\n\t\t\t\tmetadata: {\n\t\t\t\t\tversion: 4.4,\n\t\t\t\t\ttype: 'BufferGeometry',\n\t\t\t\t\tgenerator: 'BufferGeometry.toJSON'\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// standard BufferGeometry serialization\n\n\t\t\tdata.uuid = this.uuid;\n\t\t\tdata.type = this.type;\n\t\t\tif ( this.name !== '' ) data.name = this.name;\n\n\t\t\tif ( this.parameters !== undefined ) {\n\n\t\t\t\tvar parameters = this.parameters;\n\n\t\t\t\tfor ( var key in parameters ) {\n\n\t\t\t\t\tif ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n\t\t\t\t}\n\n\t\t\t\treturn data;\n\n\t\t\t}\n\n\t\t\tdata.data = { attributes: {} };\n\n\t\t\tvar index = this.index;\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tvar array = Array.prototype.slice.call( index.array );\n\n\t\t\t\tdata.data.index = {\n\t\t\t\t\ttype: index.array.constructor.name,\n\t\t\t\t\tarray: array\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\tvar attributes = this.attributes;\n\n\t\t\tfor ( var key in attributes ) {\n\n\t\t\t\tvar attribute = attributes[ key ];\n\n\t\t\t\tvar array = Array.prototype.slice.call( attribute.array );\n\n\t\t\t\tdata.data.attributes[ key ] = {\n\t\t\t\t\titemSize: attribute.itemSize,\n\t\t\t\t\ttype: attribute.array.constructor.name,\n\t\t\t\t\tarray: array,\n\t\t\t\t\tnormalized: attribute.normalized\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\tvar groups = this.groups;\n\n\t\t\tif ( groups.length > 0 ) {\n\n\t\t\t\tdata.data.groups = JSON.parse( JSON.stringify( groups ) );\n\n\t\t\t}\n\n\t\t\tvar boundingSphere = this.boundingSphere;\n\n\t\t\tif ( boundingSphere !== null ) {\n\n\t\t\t\tdata.data.boundingSphere = {\n\t\t\t\t\tcenter: boundingSphere.center.toArray(),\n\t\t\t\t\tradius: boundingSphere.radius\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\treturn data;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\t/*\n\t\t\t// Handle primitives\n\n\t\t\tvar parameters = this.parameters;\n\n\t\t\tif ( parameters !== undefined ) {\n\n\t\t\t\tvar values = [];\n\n\t\t\t\tfor ( var key in parameters ) {\n\n\t\t\t\t\tvalues.push( parameters[ key ] );\n\n\t\t\t\t}\n\n\t\t\t\tvar geometry = Object.create( this.constructor.prototype );\n\t\t\t\tthis.constructor.apply( geometry, values );\n\t\t\t\treturn geometry;\n\n\t\t\t}\n\n\t\t\treturn new this.constructor().copy( this );\n\t\t\t*/\n\n\t\t\treturn new BufferGeometry().copy( this );\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tvar index = source.index;\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tthis.setIndex( index.clone() );\n\n\t\t\t}\n\n\t\t\tvar attributes = source.attributes;\n\n\t\t\tfor ( var name in attributes ) {\n\n\t\t\t\tvar attribute = attributes[ name ];\n\t\t\t\tthis.addAttribute( name, attribute.clone() );\n\n\t\t\t}\n\n\t\t\tvar groups = source.groups;\n\n\t\t\tfor ( var i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\t\tvar group = groups[ i ];\n\t\t\t\tthis.addGroup( group.start, group.count, group.materialIndex );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tdispose: function () {\n\n\t\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t\t}\n\n\t} );\n\n\tBufferGeometry.MaxIndex = 65535;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author jonobr1 / http://jonobr1.com/\n\t */\n\n\tfunction Mesh( geometry, material ) {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Mesh';\n\n\t\tthis.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n\t\tthis.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } );\n\n\t\tthis.drawMode = TrianglesDrawMode;\n\n\t\tthis.updateMorphTargets();\n\n\t}\n\n\tMesh.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: Mesh,\n\n\t\tisMesh: true,\n\n\t\tsetDrawMode: function ( value ) {\n\n\t\t\tthis.drawMode = value;\n\n\t\t},\n\n\t\tcopy: function ( source ) {\n\n\t\t\tObject3D.prototype.copy.call( this, source );\n\n\t\t\tthis.drawMode = source.drawMode;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tupdateMorphTargets: function () {\n\n\t\t\tvar morphTargets = this.geometry.morphTargets;\n\n\t\t\tif ( morphTargets !== undefined && morphTargets.length > 0 ) {\n\n\t\t\t\tthis.morphTargetInfluences = [];\n\t\t\t\tthis.morphTargetDictionary = {};\n\n\t\t\t\tfor ( var m = 0, ml = morphTargets.length; m < ml; m ++ ) {\n\n\t\t\t\t\tthis.morphTargetInfluences.push( 0 );\n\t\t\t\t\tthis.morphTargetDictionary[ morphTargets[ m ].name ] = m;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t},\n\n\t\traycast: ( function () {\n\n\t\t\tvar inverseMatrix = new Matrix4();\n\t\t\tvar ray = new Ray();\n\t\t\tvar sphere = new Sphere();\n\n\t\t\tvar vA = new Vector3();\n\t\t\tvar vB = new Vector3();\n\t\t\tvar vC = new Vector3();\n\n\t\t\tvar tempA = new Vector3();\n\t\t\tvar tempB = new Vector3();\n\t\t\tvar tempC = new Vector3();\n\n\t\t\tvar uvA = new Vector2();\n\t\t\tvar uvB = new Vector2();\n\t\t\tvar uvC = new Vector2();\n\n\t\t\tvar barycoord = new Vector3();\n\n\t\t\tvar intersectionPoint = new Vector3();\n\t\t\tvar intersectionPointWorld = new Vector3();\n\n\t\t\tfunction uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {\n\n\t\t\t\tTriangle.barycoordFromPoint( point, p1, p2, p3, barycoord );\n\n\t\t\t\tuv1.multiplyScalar( barycoord.x );\n\t\t\t\tuv2.multiplyScalar( barycoord.y );\n\t\t\t\tuv3.multiplyScalar( barycoord.z );\n\n\t\t\t\tuv1.add( uv2 ).add( uv3 );\n\n\t\t\t\treturn uv1.clone();\n\n\t\t\t}\n\n\t\t\tfunction checkIntersection( object, raycaster, ray, pA, pB, pC, point ) {\n\n\t\t\t\tvar intersect;\n\t\t\t\tvar material = object.material;\n\n\t\t\t\tif ( material.side === BackSide ) {\n\n\t\t\t\t\tintersect = ray.intersectTriangle( pC, pB, pA, true, point );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tintersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point );\n\n\t\t\t\t}\n\n\t\t\t\tif ( intersect === null ) return null;\n\n\t\t\t\tintersectionPointWorld.copy( point );\n\t\t\t\tintersectionPointWorld.applyMatrix4( object.matrixWorld );\n\n\t\t\t\tvar distance = raycaster.ray.origin.distanceTo( intersectionPointWorld );\n\n\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) return null;\n\n\t\t\t\treturn {\n\t\t\t\t\tdistance: distance,\n\t\t\t\t\tpoint: intersectionPointWorld.clone(),\n\t\t\t\t\tobject: object\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\tfunction checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) {\n\n\t\t\t\tvA.fromArray( positions, a * 3 );\n\t\t\t\tvB.fromArray( positions, b * 3 );\n\t\t\t\tvC.fromArray( positions, c * 3 );\n\n\t\t\t\tvar intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint );\n\n\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\tif ( uvs ) {\n\n\t\t\t\t\t\tuvA.fromArray( uvs, a * 2 );\n\t\t\t\t\t\tuvB.fromArray( uvs, b * 2 );\n\t\t\t\t\t\tuvC.fromArray( uvs, c * 2 );\n\n\t\t\t\t\t\tintersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tintersection.face = new Face3( a, b, c, Triangle.normal( vA, vB, vC ) );\n\t\t\t\t\tintersection.faceIndex = a;\n\n\t\t\t\t}\n\n\t\t\t\treturn intersection;\n\n\t\t\t}\n\n\t\t\treturn function raycast( raycaster, intersects ) {\n\n\t\t\t\tvar geometry = this.geometry;\n\t\t\t\tvar material = this.material;\n\t\t\t\tvar matrixWorld = this.matrixWorld;\n\n\t\t\t\tif ( material === undefined ) return;\n\n\t\t\t\t// Checking boundingSphere distance to ray\n\n\t\t\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t\t\tsphere.copy( geometry.boundingSphere );\n\t\t\t\tsphere.applyMatrix4( matrixWorld );\n\n\t\t\t\tif ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n\t\t\t\t//\n\n\t\t\t\tinverseMatrix.getInverse( matrixWorld );\n\t\t\t\tray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n\t\t\t\t// Check boundingBox before continuing\n\n\t\t\t\tif ( geometry.boundingBox !== null ) {\n\n\t\t\t\t\tif ( ray.intersectsBox( geometry.boundingBox ) === false ) return;\n\n\t\t\t\t}\n\n\t\t\t\tvar uvs, intersection;\n\n\t\t\t\tif ( (geometry && geometry.isBufferGeometry) ) {\n\n\t\t\t\t\tvar a, b, c;\n\t\t\t\t\tvar index = geometry.index;\n\t\t\t\t\tvar attributes = geometry.attributes;\n\t\t\t\t\tvar positions = attributes.position.array;\n\n\t\t\t\t\tif ( attributes.uv !== undefined ) {\n\n\t\t\t\t\t\tuvs = attributes.uv.array;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( index !== null ) {\n\n\t\t\t\t\t\tvar indices = index.array;\n\n\t\t\t\t\t\tfor ( var i = 0, l = indices.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\ta = indices[ i ];\n\t\t\t\t\t\t\tb = indices[ i + 1 ];\n\t\t\t\t\t\t\tc = indices[ i + 2 ];\n\n\t\t\t\t\t\t\tintersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );\n\n\t\t\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\t\t\tintersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics\n\t\t\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\n\t\t\t\t\t\tfor ( var i = 0, l = positions.length; i < l; i += 9 ) {\n\n\t\t\t\t\t\t\ta = i / 3;\n\t\t\t\t\t\t\tb = a + 1;\n\t\t\t\t\t\t\tc = a + 2;\n\n\t\t\t\t\t\t\tintersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );\n\n\t\t\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\t\t\tintersection.index = a; // triangle number in positions buffer semantics\n\t\t\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( (geometry && geometry.isGeometry) ) {\n\n\t\t\t\t\tvar fvA, fvB, fvC;\n\t\t\t\t\tvar isFaceMaterial = (material && material.isMultiMaterial);\n\t\t\t\t\tvar materials = isFaceMaterial === true ? material.materials : null;\n\n\t\t\t\t\tvar vertices = geometry.vertices;\n\t\t\t\t\tvar faces = geometry.faces;\n\t\t\t\t\tvar faceVertexUvs = geometry.faceVertexUvs[ 0 ];\n\t\t\t\t\tif ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;\n\n\t\t\t\t\tfor ( var f = 0, fl = faces.length; f < fl; f ++ ) {\n\n\t\t\t\t\t\tvar face = faces[ f ];\n\t\t\t\t\t\tvar faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material;\n\n\t\t\t\t\t\tif ( faceMaterial === undefined ) continue;\n\n\t\t\t\t\t\tfvA = vertices[ face.a ];\n\t\t\t\t\t\tfvB = vertices[ face.b ];\n\t\t\t\t\t\tfvC = vertices[ face.c ];\n\n\t\t\t\t\t\tif ( faceMaterial.morphTargets === true ) {\n\n\t\t\t\t\t\t\tvar morphTargets = geometry.morphTargets;\n\t\t\t\t\t\t\tvar morphInfluences = this.morphTargetInfluences;\n\n\t\t\t\t\t\t\tvA.set( 0, 0, 0 );\n\t\t\t\t\t\t\tvB.set( 0, 0, 0 );\n\t\t\t\t\t\t\tvC.set( 0, 0, 0 );\n\n\t\t\t\t\t\t\tfor ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {\n\n\t\t\t\t\t\t\t\tvar influence = morphInfluences[ t ];\n\n\t\t\t\t\t\t\t\tif ( influence === 0 ) continue;\n\n\t\t\t\t\t\t\t\tvar targets = morphTargets[ t ].vertices;\n\n\t\t\t\t\t\t\t\tvA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence );\n\t\t\t\t\t\t\t\tvB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence );\n\t\t\t\t\t\t\t\tvC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tvA.add( fvA );\n\t\t\t\t\t\t\tvB.add( fvB );\n\t\t\t\t\t\t\tvC.add( fvC );\n\n\t\t\t\t\t\t\tfvA = vA;\n\t\t\t\t\t\t\tfvB = vB;\n\t\t\t\t\t\t\tfvC = vC;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tintersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint );\n\n\t\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\t\tif ( uvs ) {\n\n\t\t\t\t\t\t\t\tvar uvs_f = uvs[ f ];\n\t\t\t\t\t\t\t\tuvA.copy( uvs_f[ 0 ] );\n\t\t\t\t\t\t\t\tuvB.copy( uvs_f[ 1 ] );\n\t\t\t\t\t\t\t\tuvC.copy( uvs_f[ 2 ] );\n\n\t\t\t\t\t\t\t\tintersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tintersection.face = face;\n\t\t\t\t\t\t\tintersection.faceIndex = f;\n\t\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}() ),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.geometry, this.material ).copy( this );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'BoxBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\tdepth: depth,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\tdepthSegments: depthSegments\n\t\t};\n\n\t\tvar scope = this;\n\n\t\t// segments\n\t\twidthSegments = Math.floor( widthSegments ) || 1;\n\t\theightSegments = Math.floor( heightSegments ) || 1;\n\t\tdepthSegments = Math.floor( depthSegments ) || 1;\n\n\t\t// these are used to calculate buffer length\n\t\tvar vertexCount = calculateVertexCount( widthSegments, heightSegments, depthSegments );\n\t\tvar indexCount = calculateIndexCount( widthSegments, heightSegments, depthSegments );\n\n\t\t// buffers\n\t\tvar indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount );\n\t\tvar vertices = new Float32Array( vertexCount * 3 );\n\t\tvar normals = new Float32Array( vertexCount * 3 );\n\t\tvar uvs = new Float32Array( vertexCount * 2 );\n\n\t\t// offset variables\n\t\tvar vertexBufferOffset = 0;\n\t\tvar uvBufferOffset = 0;\n\t\tvar indexBufferOffset = 0;\n\t\tvar numberOfVertices = 0;\n\n\t\t// group variables\n\t\tvar groupStart = 0;\n\n\t\t// build each side of the box geometry\n\t\tbuildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px\n\t\tbuildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx\n\t\tbuildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py\n\t\tbuildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny\n\t\tbuildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz\n\t\tbuildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz\n\n\t\t// build geometry\n\t\tthis.setIndex( new BufferAttribute( indices, 1 ) );\n\t\tthis.addAttribute( 'position', new BufferAttribute( vertices, 3 ) );\n\t\tthis.addAttribute( 'normal', new BufferAttribute( normals, 3 ) );\n\t\tthis.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) );\n\n\t\t// helper functions\n\n\t\tfunction calculateVertexCount( w, h, d ) {\n\n\t\t\tvar vertices = 0;\n\n\t\t\t// calculate the amount of vertices for each side (plane)\n\t\t\tvertices += (w + 1) * (h + 1) * 2; // xy\n\t\t\tvertices += (w + 1) * (d + 1) * 2; // xz\n\t\t\tvertices += (d + 1) * (h + 1) * 2; // zy\n\n\t\t\treturn vertices;\n\n\t\t}\n\n\t\tfunction calculateIndexCount( w, h, d ) {\n\n\t\t\tvar index = 0;\n\n\t\t\t// calculate the amount of squares for each side\n\t\t\tindex += w * h * 2; // xy\n\t\t\tindex += w * d * 2; // xz\n\t\t\tindex += d * h * 2; // zy\n\n\t\t\treturn index * 6; // two triangles per square => six vertices per square\n\n\t\t}\n\n\t\tfunction buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {\n\n\t\t\tvar segmentWidth\t= width / gridX;\n\t\t\tvar segmentHeight = height / gridY;\n\n\t\t\tvar widthHalf = width / 2;\n\t\t\tvar heightHalf = height / 2;\n\t\t\tvar depthHalf = depth / 2;\n\n\t\t\tvar gridX1 = gridX + 1;\n\t\t\tvar gridY1 = gridY + 1;\n\n\t\t\tvar vertexCounter = 0;\n\t\t\tvar groupCount = 0;\n\n\t\t\tvar vector = new Vector3();\n\n\t\t\t// generate vertices, normals and uvs\n\n\t\t\tfor ( var iy = 0; iy < gridY1; iy ++ ) {\n\n\t\t\t\tvar y = iy * segmentHeight - heightHalf;\n\n\t\t\t\tfor ( var ix = 0; ix < gridX1; ix ++ ) {\n\n\t\t\t\t\tvar x = ix * segmentWidth - widthHalf;\n\n\t\t\t\t\t// set values to correct vector component\n\t\t\t\t\tvector[ u ] = x * udir;\n\t\t\t\t\tvector[ v ] = y * vdir;\n\t\t\t\t\tvector[ w ] = depthHalf;\n\n\t\t\t\t\t// now apply vector to vertex buffer\n\t\t\t\t\tvertices[ vertexBufferOffset ] = vector.x;\n\t\t\t\t\tvertices[ vertexBufferOffset + 1 ] = vector.y;\n\t\t\t\t\tvertices[ vertexBufferOffset + 2 ] = vector.z;\n\n\t\t\t\t\t// set values to correct vector component\n\t\t\t\t\tvector[ u ] = 0;\n\t\t\t\t\tvector[ v ] = 0;\n\t\t\t\t\tvector[ w ] = depth > 0 ? 1 : - 1;\n\n\t\t\t\t\t// now apply vector to normal buffer\n\t\t\t\t\tnormals[ vertexBufferOffset ] = vector.x;\n\t\t\t\t\tnormals[ vertexBufferOffset + 1 ] = vector.y;\n\t\t\t\t\tnormals[ vertexBufferOffset + 2 ] = vector.z;\n\n\t\t\t\t\t// uvs\n\t\t\t\t\tuvs[ uvBufferOffset ] = ix / gridX;\n\t\t\t\t\tuvs[ uvBufferOffset + 1 ] = 1 - ( iy / gridY );\n\n\t\t\t\t\t// update offsets and counters\n\t\t\t\t\tvertexBufferOffset += 3;\n\t\t\t\t\tuvBufferOffset += 2;\n\t\t\t\t\tvertexCounter += 1;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// 1. you need three indices to draw a single face\n\t\t\t// 2. a single segment consists of two faces\n\t\t\t// 3. so we need to generate six (2*3) indices per segment\n\n\t\t\tfor ( iy = 0; iy < gridY; iy ++ ) {\n\n\t\t\t\tfor ( ix = 0; ix < gridX; ix ++ ) {\n\n\t\t\t\t\t// indices\n\t\t\t\t\tvar a = numberOfVertices + ix + gridX1 * iy;\n\t\t\t\t\tvar b = numberOfVertices + ix + gridX1 * ( iy + 1 );\n\t\t\t\t\tvar c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );\n\t\t\t\t\tvar d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;\n\n\t\t\t\t\t// face one\n\t\t\t\t\tindices[ indexBufferOffset ] = a;\n\t\t\t\t\tindices[ indexBufferOffset + 1 ] = b;\n\t\t\t\t\tindices[ indexBufferOffset + 2 ] = d;\n\n\t\t\t\t\t// face two\n\t\t\t\t\tindices[ indexBufferOffset + 3 ] = b;\n\t\t\t\t\tindices[ indexBufferOffset + 4 ] = c;\n\t\t\t\t\tindices[ indexBufferOffset + 5 ] = d;\n\n\t\t\t\t\t// update offsets and counters\n\t\t\t\t\tindexBufferOffset += 6;\n\t\t\t\t\tgroupCount += 6;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\t\t\tscope.addGroup( groupStart, groupCount, materialIndex );\n\n\t\t\t// calculate new start value for groups\n\t\t\tgroupStart += groupCount;\n\n\t\t\t// update total number of vertices\n\t\t\tnumberOfVertices += vertexCounter;\n\n\t\t}\n\n\t}\n\n\tBoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tBoxBufferGeometry.prototype.constructor = BoxBufferGeometry;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as\n\t */\n\n\tfunction PlaneBufferGeometry( width, height, widthSegments, heightSegments ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'PlaneBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments\n\t\t};\n\n\t\tvar width_half = width / 2;\n\t\tvar height_half = height / 2;\n\n\t\tvar gridX = Math.floor( widthSegments ) || 1;\n\t\tvar gridY = Math.floor( heightSegments ) || 1;\n\n\t\tvar gridX1 = gridX + 1;\n\t\tvar gridY1 = gridY + 1;\n\n\t\tvar segment_width = width / gridX;\n\t\tvar segment_height = height / gridY;\n\n\t\tvar vertices = new Float32Array( gridX1 * gridY1 * 3 );\n\t\tvar normals = new Float32Array( gridX1 * gridY1 * 3 );\n\t\tvar uvs = new Float32Array( gridX1 * gridY1 * 2 );\n\n\t\tvar offset = 0;\n\t\tvar offset2 = 0;\n\n\t\tfor ( var iy = 0; iy < gridY1; iy ++ ) {\n\n\t\t\tvar y = iy * segment_height - height_half;\n\n\t\t\tfor ( var ix = 0; ix < gridX1; ix ++ ) {\n\n\t\t\t\tvar x = ix * segment_width - width_half;\n\n\t\t\t\tvertices[ offset ] = x;\n\t\t\t\tvertices[ offset + 1 ] = - y;\n\n\t\t\t\tnormals[ offset + 2 ] = 1;\n\n\t\t\t\tuvs[ offset2 ] = ix / gridX;\n\t\t\t\tuvs[ offset2 + 1 ] = 1 - ( iy / gridY );\n\n\t\t\t\toffset += 3;\n\t\t\t\toffset2 += 2;\n\n\t\t\t}\n\n\t\t}\n\n\t\toffset = 0;\n\n\t\tvar indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );\n\n\t\tfor ( var iy = 0; iy < gridY; iy ++ ) {\n\n\t\t\tfor ( var ix = 0; ix < gridX; ix ++ ) {\n\n\t\t\t\tvar a = ix + gridX1 * iy;\n\t\t\t\tvar b = ix + gridX1 * ( iy + 1 );\n\t\t\t\tvar c = ( ix + 1 ) + gridX1 * ( iy + 1 );\n\t\t\t\tvar d = ( ix + 1 ) + gridX1 * iy;\n\n\t\t\t\tindices[ offset ] = a;\n\t\t\t\tindices[ offset + 1 ] = b;\n\t\t\t\tindices[ offset + 2 ] = d;\n\n\t\t\t\tindices[ offset + 3 ] = b;\n\t\t\t\tindices[ offset + 4 ] = c;\n\t\t\t\tindices[ offset + 5 ] = d;\n\n\t\t\t\toffset += 6;\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.setIndex( new BufferAttribute( indices, 1 ) );\n\t\tthis.addAttribute( 'position', new BufferAttribute( vertices, 3 ) );\n\t\tthis.addAttribute( 'normal', new BufferAttribute( normals, 3 ) );\n\t\tthis.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tPlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tPlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author WestLangley / http://github.com/WestLangley\n\t*/\n\n\tfunction Camera() {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Camera';\n\n\t\tthis.matrixWorldInverse = new Matrix4();\n\t\tthis.projectionMatrix = new Matrix4();\n\n\t}\n\n\tCamera.prototype = Object.create( Object3D.prototype );\n\tCamera.prototype.constructor = Camera;\n\n\tCamera.prototype.isCamera = true;\n\n\tCamera.prototype.getWorldDirection = function () {\n\n\t\tvar quaternion = new Quaternion();\n\n\t\treturn function getWorldDirection( optionalTarget ) {\n\n\t\t\tvar result = optionalTarget || new Vector3();\n\n\t\t\tthis.getWorldQuaternion( quaternion );\n\n\t\t\treturn result.set( 0, 0, - 1 ).applyQuaternion( quaternion );\n\n\t\t};\n\n\t}();\n\n\tCamera.prototype.lookAt = function () {\n\n\t\t// This routine does not support cameras with rotated and/or translated parent(s)\n\n\t\tvar m1 = new Matrix4();\n\n\t\treturn function lookAt( vector ) {\n\n\t\t\tm1.lookAt( this.position, vector, this.up );\n\n\t\t\tthis.quaternion.setFromRotationMatrix( m1 );\n\n\t\t};\n\n\t}();\n\n\tCamera.prototype.clone = function () {\n\n\t\treturn new this.constructor().copy( this );\n\n\t};\n\n\tCamera.prototype.copy = function ( source ) {\n\n\t\tObject3D.prototype.copy.call( this, source );\n\n\t\tthis.matrixWorldInverse.copy( source.matrixWorldInverse );\n\t\tthis.projectionMatrix.copy( source.projectionMatrix );\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author greggman / http://games.greggman.com/\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t * @author tschw\n\t */\n\n\tfunction PerspectiveCamera( fov, aspect, near, far ) {\n\n\t\tCamera.call( this );\n\n\t\tthis.type = 'PerspectiveCamera';\n\n\t\tthis.fov = fov !== undefined ? fov : 50;\n\t\tthis.zoom = 1;\n\n\t\tthis.near = near !== undefined ? near : 0.1;\n\t\tthis.far = far !== undefined ? far : 2000;\n\t\tthis.focus = 10;\n\n\t\tthis.aspect = aspect !== undefined ? aspect : 1;\n\t\tthis.view = null;\n\n\t\tthis.filmGauge = 35;\t// width of the film (default in millimeters)\n\t\tthis.filmOffset = 0;\t// horizontal film offset (same unit as gauge)\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tPerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {\n\n\t\tconstructor: PerspectiveCamera,\n\n\t\tisPerspectiveCamera: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tCamera.prototype.copy.call( this, source );\n\n\t\t\tthis.fov = source.fov;\n\t\t\tthis.zoom = source.zoom;\n\n\t\t\tthis.near = source.near;\n\t\t\tthis.far = source.far;\n\t\t\tthis.focus = source.focus;\n\n\t\t\tthis.aspect = source.aspect;\n\t\t\tthis.view = source.view === null ? null : Object.assign( {}, source.view );\n\n\t\t\tthis.filmGauge = source.filmGauge;\n\t\t\tthis.filmOffset = source.filmOffset;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t/**\n\t\t * Sets the FOV by focal length in respect to the current .filmGauge.\n\t\t *\n\t\t * The default film gauge is 35, so that the focal length can be specified for\n\t\t * a 35mm (full frame) camera.\n\t\t *\n\t\t * Values for focal length and film gauge must have the same unit.\n\t\t */\n\t\tsetFocalLength: function ( focalLength ) {\n\n\t\t\t// see http://www.bobatkins.com/photography/technical/field_of_view.html\n\t\t\tvar vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;\n\n\t\t\tthis.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope );\n\t\t\tthis.updateProjectionMatrix();\n\n\t\t},\n\n\t\t/**\n\t\t * Calculates the focal length from the current .fov and .filmGauge.\n\t\t */\n\t\tgetFocalLength: function () {\n\n\t\t\tvar vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov );\n\n\t\t\treturn 0.5 * this.getFilmHeight() / vExtentSlope;\n\n\t\t},\n\n\t\tgetEffectiveFOV: function () {\n\n\t\t\treturn _Math.RAD2DEG * 2 * Math.atan(\n\t\t\t\t\tMath.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom );\n\n\t\t},\n\n\t\tgetFilmWidth: function () {\n\n\t\t\t// film not completely covered in portrait format (aspect < 1)\n\t\t\treturn this.filmGauge * Math.min( this.aspect, 1 );\n\n\t\t},\n\n\t\tgetFilmHeight: function () {\n\n\t\t\t// film not completely covered in landscape format (aspect > 1)\n\t\t\treturn this.filmGauge / Math.max( this.aspect, 1 );\n\n\t\t},\n\n\t\t/**\n\t\t * Sets an offset in a larger frustum. This is useful for multi-window or\n\t\t * multi-monitor/multi-machine setups.\n\t\t *\n\t\t * For example, if you have 3x2 monitors and each monitor is 1920x1080 and\n\t\t * the monitors are in grid like this\n\t\t *\n\t\t * +---+---+---+\n\t\t * | A | B | C |\n\t\t * +---+---+---+\n\t\t * | D | E | F |\n\t\t * +---+---+---+\n\t\t *\n\t\t * then for each monitor you would call it like this\n\t\t *\n\t\t * var w = 1920;\n\t\t * var h = 1080;\n\t\t * var fullWidth = w * 3;\n\t\t * var fullHeight = h * 2;\n\t\t *\n\t\t * --A--\n\t\t * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );\n\t\t * --B--\n\t\t * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );\n\t\t * --C--\n\t\t * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );\n\t\t * --D--\n\t\t * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );\n\t\t * --E--\n\t\t * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );\n\t\t * --F--\n\t\t * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );\n\t\t *\n\t\t * Note there is no reason monitors have to be the same size or in a grid.\n\t\t */\n\t\tsetViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {\n\n\t\t\tthis.aspect = fullWidth / fullHeight;\n\n\t\t\tthis.view = {\n\t\t\t\tfullWidth: fullWidth,\n\t\t\t\tfullHeight: fullHeight,\n\t\t\t\toffsetX: x,\n\t\t\t\toffsetY: y,\n\t\t\t\twidth: width,\n\t\t\t\theight: height\n\t\t\t};\n\n\t\t\tthis.updateProjectionMatrix();\n\n\t\t},\n\n\t\tclearViewOffset: function() {\n\n\t\t\tthis.view = null;\n\t\t\tthis.updateProjectionMatrix();\n\n\t\t},\n\n\t\tupdateProjectionMatrix: function () {\n\n\t\t\tvar near = this.near,\n\t\t\t\ttop = near * Math.tan(\n\t\t\t\t\t\t_Math.DEG2RAD * 0.5 * this.fov ) / this.zoom,\n\t\t\t\theight = 2 * top,\n\t\t\t\twidth = this.aspect * height,\n\t\t\t\tleft = - 0.5 * width,\n\t\t\t\tview = this.view;\n\n\t\t\tif ( view !== null ) {\n\n\t\t\t\tvar fullWidth = view.fullWidth,\n\t\t\t\t\tfullHeight = view.fullHeight;\n\n\t\t\t\tleft += view.offsetX * width / fullWidth;\n\t\t\t\ttop -= view.offsetY * height / fullHeight;\n\t\t\t\twidth *= view.width / fullWidth;\n\t\t\t\theight *= view.height / fullHeight;\n\n\t\t\t}\n\n\t\t\tvar skew = this.filmOffset;\n\t\t\tif ( skew !== 0 ) left += near * skew / this.getFilmWidth();\n\n\t\t\tthis.projectionMatrix.makeFrustum(\n\t\t\t\t\tleft, left + width, top - height, top, near, this.far );\n\n\t\t},\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\tvar data = Object3D.prototype.toJSON.call( this, meta );\n\n\t\t\tdata.object.fov = this.fov;\n\t\t\tdata.object.zoom = this.zoom;\n\n\t\t\tdata.object.near = this.near;\n\t\t\tdata.object.far = this.far;\n\t\t\tdata.object.focus = this.focus;\n\n\t\t\tdata.object.aspect = this.aspect;\n\n\t\t\tif ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n\t\t\tdata.object.filmGauge = this.filmGauge;\n\t\t\tdata.object.filmOffset = this.filmOffset;\n\n\t\t\treturn data;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author arose / http://github.com/arose\n\t */\n\n\tfunction OrthographicCamera( left, right, top, bottom, near, far ) {\n\n\t\tCamera.call( this );\n\n\t\tthis.type = 'OrthographicCamera';\n\n\t\tthis.zoom = 1;\n\t\tthis.view = null;\n\n\t\tthis.left = left;\n\t\tthis.right = right;\n\t\tthis.top = top;\n\t\tthis.bottom = bottom;\n\n\t\tthis.near = ( near !== undefined ) ? near : 0.1;\n\t\tthis.far = ( far !== undefined ) ? far : 2000;\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tOrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), {\n\n\t\tconstructor: OrthographicCamera,\n\n\t\tisOrthographicCamera: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tCamera.prototype.copy.call( this, source );\n\n\t\t\tthis.left = source.left;\n\t\t\tthis.right = source.right;\n\t\t\tthis.top = source.top;\n\t\t\tthis.bottom = source.bottom;\n\t\t\tthis.near = source.near;\n\t\t\tthis.far = source.far;\n\n\t\t\tthis.zoom = source.zoom;\n\t\t\tthis.view = source.view === null ? null : Object.assign( {}, source.view );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetViewOffset: function( fullWidth, fullHeight, x, y, width, height ) {\n\n\t\t\tthis.view = {\n\t\t\t\tfullWidth: fullWidth,\n\t\t\t\tfullHeight: fullHeight,\n\t\t\t\toffsetX: x,\n\t\t\t\toffsetY: y,\n\t\t\t\twidth: width,\n\t\t\t\theight: height\n\t\t\t};\n\n\t\t\tthis.updateProjectionMatrix();\n\n\t\t},\n\n\t\tclearViewOffset: function() {\n\n\t\t\tthis.view = null;\n\t\t\tthis.updateProjectionMatrix();\n\n\t\t},\n\n\t\tupdateProjectionMatrix: function () {\n\n\t\t\tvar dx = ( this.right - this.left ) / ( 2 * this.zoom );\n\t\t\tvar dy = ( this.top - this.bottom ) / ( 2 * this.zoom );\n\t\t\tvar cx = ( this.right + this.left ) / 2;\n\t\t\tvar cy = ( this.top + this.bottom ) / 2;\n\n\t\t\tvar left = cx - dx;\n\t\t\tvar right = cx + dx;\n\t\t\tvar top = cy + dy;\n\t\t\tvar bottom = cy - dy;\n\n\t\t\tif ( this.view !== null ) {\n\n\t\t\t\tvar zoomW = this.zoom / ( this.view.width / this.view.fullWidth );\n\t\t\t\tvar zoomH = this.zoom / ( this.view.height / this.view.fullHeight );\n\t\t\t\tvar scaleW = ( this.right - this.left ) / this.view.width;\n\t\t\t\tvar scaleH = ( this.top - this.bottom ) / this.view.height;\n\n\t\t\t\tleft += scaleW * ( this.view.offsetX / zoomW );\n\t\t\t\tright = left + scaleW * ( this.view.width / zoomW );\n\t\t\t\ttop -= scaleH * ( this.view.offsetY / zoomH );\n\t\t\t\tbottom = top - scaleH * ( this.view.height / zoomH );\n\n\t\t\t}\n\n\t\t\tthis.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far );\n\n\t\t},\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\tvar data = Object3D.prototype.toJSON.call( this, meta );\n\n\t\t\tdata.object.zoom = this.zoom;\n\t\t\tdata.object.left = this.left;\n\t\t\tdata.object.right = this.right;\n\t\t\tdata.object.top = this.top;\n\t\t\tdata.object.bottom = this.bottom;\n\t\t\tdata.object.near = this.near;\n\t\t\tdata.object.far = this.far;\n\n\t\t\tif ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n\t\t\treturn data;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLIndexedBufferRenderer( gl, extensions, infoRender ) {\n\n\t\tvar mode;\n\n\t\tfunction setMode( value ) {\n\n\t\t\tmode = value;\n\n\t\t}\n\n\t\tvar type, size;\n\n\t\tfunction setIndex( index ) {\n\n\t\t\tif ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) {\n\n\t\t\t\ttype = gl.UNSIGNED_INT;\n\t\t\t\tsize = 4;\n\n\t\t\t} else {\n\n\t\t\t\ttype = gl.UNSIGNED_SHORT;\n\t\t\t\tsize = 2;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction render( start, count ) {\n\n\t\t\tgl.drawElements( mode, count, type, start * size );\n\n\t\t\tinfoRender.calls ++;\n\t\t\tinfoRender.vertices += count;\n\n\t\t\tif ( mode === gl.TRIANGLES ) infoRender.faces += count / 3;\n\n\t\t}\n\n\t\tfunction renderInstances( geometry, start, count ) {\n\n\t\t\tvar extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\textension.drawElementsInstancedANGLE( mode, count, type, start * size, geometry.maxInstancedCount );\n\n\t\t\tinfoRender.calls ++;\n\t\t\tinfoRender.vertices += count * geometry.maxInstancedCount;\n\n\t\t\tif ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3;\n\n\t\t}\n\n\t\treturn {\n\n\t\t\tsetMode: setMode,\n\t\t\tsetIndex: setIndex,\n\t\t\trender: render,\n\t\t\trenderInstances: renderInstances\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLBufferRenderer( gl, extensions, infoRender ) {\n\n\t\tvar mode;\n\n\t\tfunction setMode( value ) {\n\n\t\t\tmode = value;\n\n\t\t}\n\n\t\tfunction render( start, count ) {\n\n\t\t\tgl.drawArrays( mode, start, count );\n\n\t\t\tinfoRender.calls ++;\n\t\t\tinfoRender.vertices += count;\n\n\t\t\tif ( mode === gl.TRIANGLES ) infoRender.faces += count / 3;\n\n\t\t}\n\n\t\tfunction renderInstances( geometry ) {\n\n\t\t\tvar extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tvar position = geometry.attributes.position;\n\n\t\t\tvar count = 0;\n\n\t\t\tif ( (position && position.isInterleavedBufferAttribute) ) {\n\n\t\t\t\tcount = position.data.count;\n\n\t\t\t\textension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount );\n\n\t\t\t} else {\n\n\t\t\t\tcount = position.count;\n\n\t\t\t\textension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount );\n\n\t\t\t}\n\n\t\t\tinfoRender.calls ++;\n\t\t\tinfoRender.vertices += count * geometry.maxInstancedCount;\n\n\t\t\tif ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3;\n\n\t\t}\n\n\t\treturn {\n\t\t\tsetMode: setMode,\n\t\t\trender: render,\n\t\t\trenderInstances: renderInstances\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLLights() {\n\n\t\tvar lights = {};\n\n\t\treturn {\n\n\t\t\tget: function ( light ) {\n\n\t\t\t\tif ( lights[ light.id ] !== undefined ) {\n\n\t\t\t\t\treturn lights[ light.id ];\n\n\t\t\t\t}\n\n\t\t\t\tvar uniforms;\n\n\t\t\t\tswitch ( light.type ) {\n\n\t\t\t\t\tcase 'DirectionalLight':\n\t\t\t\t\t\tuniforms = {\n\t\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\t\tcolor: new Color(),\n\n\t\t\t\t\t\t\tshadow: false,\n\t\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\t\tshadowMapSize: new Vector2()\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'SpotLight':\n\t\t\t\t\t\tuniforms = {\n\t\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\t\tdistance: 0,\n\t\t\t\t\t\t\tconeCos: 0,\n\t\t\t\t\t\t\tpenumbraCos: 0,\n\t\t\t\t\t\t\tdecay: 0,\n\n\t\t\t\t\t\t\tshadow: false,\n\t\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\t\tshadowMapSize: new Vector2()\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'PointLight':\n\t\t\t\t\t\tuniforms = {\n\t\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\t\tdistance: 0,\n\t\t\t\t\t\t\tdecay: 0,\n\n\t\t\t\t\t\t\tshadow: false,\n\t\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\t\tshadowMapSize: new Vector2()\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'HemisphereLight':\n\t\t\t\t\t\tuniforms = {\n\t\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\t\tskyColor: new Color(),\n\t\t\t\t\t\t\tgroundColor: new Color()\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t\tlights[ light.id ] = uniforms;\n\n\t\t\t\treturn uniforms;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction addLineNumbers( string ) {\n\n\t\tvar lines = string.split( '\\n' );\n\n\t\tfor ( var i = 0; i < lines.length; i ++ ) {\n\n\t\t\tlines[ i ] = ( i + 1 ) + ': ' + lines[ i ];\n\n\t\t}\n\n\t\treturn lines.join( '\\n' );\n\n\t}\n\n\tfunction WebGLShader( gl, type, string ) {\n\n\t\tvar shader = gl.createShader( type );\n\n\t\tgl.shaderSource( shader, string );\n\t\tgl.compileShader( shader );\n\n\t\tif ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {\n\n\t\t\tconsole.error( 'THREE.WebGLShader: Shader couldn\\'t compile.' );\n\n\t\t}\n\n\t\tif ( gl.getShaderInfoLog( shader ) !== '' ) {\n\n\t\t\tconsole.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );\n\n\t\t}\n\n\t\t// --enable-privileged-webgl-extension\n\t\t// console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );\n\n\t\treturn shader;\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tvar programIdCount = 0;\n\n\tfunction getEncodingComponents( encoding ) {\n\n\t\tswitch ( encoding ) {\n\n\t\t\tcase LinearEncoding:\n\t\t\t\treturn [ 'Linear','( value )' ];\n\t\t\tcase sRGBEncoding:\n\t\t\t\treturn [ 'sRGB','( value )' ];\n\t\t\tcase RGBEEncoding:\n\t\t\t\treturn [ 'RGBE','( value )' ];\n\t\t\tcase RGBM7Encoding:\n\t\t\t\treturn [ 'RGBM','( value, 7.0 )' ];\n\t\t\tcase RGBM16Encoding:\n\t\t\t\treturn [ 'RGBM','( value, 16.0 )' ];\n\t\t\tcase RGBDEncoding:\n\t\t\t\treturn [ 'RGBD','( value, 256.0 )' ];\n\t\t\tcase GammaEncoding:\n\t\t\t\treturn [ 'Gamma','( value, float( GAMMA_FACTOR ) )' ];\n\t\t\tdefault:\n\t\t\t\tthrow new Error( 'unsupported encoding: ' + encoding );\n\n\t\t}\n\n\t}\n\n\tfunction getTexelDecodingFunction( functionName, encoding ) {\n\n\t\tvar components = getEncodingComponents( encoding );\n\t\treturn \"vec4 \" + functionName + \"( vec4 value ) { return \" + components[ 0 ] + \"ToLinear\" + components[ 1 ] + \"; }\";\n\n\t}\n\n\tfunction getTexelEncodingFunction( functionName, encoding ) {\n\n\t\tvar components = getEncodingComponents( encoding );\n\t\treturn \"vec4 \" + functionName + \"( vec4 value ) { return LinearTo\" + components[ 0 ] + components[ 1 ] + \"; }\";\n\n\t}\n\n\tfunction getToneMappingFunction( functionName, toneMapping ) {\n\n\t\tvar toneMappingName;\n\n\t\tswitch ( toneMapping ) {\n\n\t\t\tcase LinearToneMapping:\n\t\t\t\ttoneMappingName = \"Linear\";\n\t\t\t\tbreak;\n\n\t\t\tcase ReinhardToneMapping:\n\t\t\t\ttoneMappingName = \"Reinhard\";\n\t\t\t\tbreak;\n\n\t\t\tcase Uncharted2ToneMapping:\n\t\t\t\ttoneMappingName = \"Uncharted2\";\n\t\t\t\tbreak;\n\n\t\t\tcase CineonToneMapping:\n\t\t\t\ttoneMappingName = \"OptimizedCineon\";\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error( 'unsupported toneMapping: ' + toneMapping );\n\n\t\t}\n\n\t\treturn \"vec3 \" + functionName + \"( vec3 color ) { return \" + toneMappingName + \"ToneMapping( color ); }\";\n\n\t}\n\n\tfunction generateExtensions( extensions, parameters, rendererExtensions ) {\n\n\t\textensions = extensions || {};\n\n\t\tvar chunks = [\n\t\t\t( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',\n\t\t\t( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',\n\t\t\t( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',\n\t\t\t( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '',\n\t\t];\n\n\t\treturn chunks.filter( filterEmptyLine ).join( '\\n' );\n\n\t}\n\n\tfunction generateDefines( defines ) {\n\n\t\tvar chunks = [];\n\n\t\tfor ( var name in defines ) {\n\n\t\t\tvar value = defines[ name ];\n\n\t\t\tif ( value === false ) continue;\n\n\t\t\tchunks.push( '#define ' + name + ' ' + value );\n\n\t\t}\n\n\t\treturn chunks.join( '\\n' );\n\n\t}\n\n\tfunction fetchAttributeLocations( gl, program, identifiers ) {\n\n\t\tvar attributes = {};\n\n\t\tvar n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );\n\n\t\tfor ( var i = 0; i < n; i ++ ) {\n\n\t\t\tvar info = gl.getActiveAttrib( program, i );\n\t\t\tvar name = info.name;\n\n\t\t\t// console.log(\"THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:\", name, i );\n\n\t\t\tattributes[ name ] = gl.getAttribLocation( program, name );\n\n\t\t}\n\n\t\treturn attributes;\n\n\t}\n\n\tfunction filterEmptyLine( string ) {\n\n\t\treturn string !== '';\n\n\t}\n\n\tfunction replaceLightNums( string, parameters ) {\n\n\t\treturn string\n\t\t\t.replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )\n\t\t\t.replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )\n\t\t\t.replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )\n\t\t\t.replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights );\n\n\t}\n\n\tfunction parseIncludes( string ) {\n\n\t\tvar pattern = /#include +<([\\w\\d.]+)>/g;\n\n\t\tfunction replace( match, include ) {\n\n\t\t\tvar replace = ShaderChunk[ include ];\n\n\t\t\tif ( replace === undefined ) {\n\n\t\t\t\tthrow new Error( 'Can not resolve #include <' + include + '>' );\n\n\t\t\t}\n\n\t\t\treturn parseIncludes( replace );\n\n\t\t}\n\n\t\treturn string.replace( pattern, replace );\n\n\t}\n\n\tfunction unrollLoops( string ) {\n\n\t\tvar pattern = /for \\( int i \\= (\\d+)\\; i < (\\d+)\\; i \\+\\+ \\) \\{([\\s\\S]+?)(?=\\})\\}/g;\n\n\t\tfunction replace( match, start, end, snippet ) {\n\n\t\t\tvar unroll = '';\n\n\t\t\tfor ( var i = parseInt( start ); i < parseInt( end ); i ++ ) {\n\n\t\t\t\tunroll += snippet.replace( /\\[ i \\]/g, '[ ' + i + ' ]' );\n\n\t\t\t}\n\n\t\t\treturn unroll;\n\n\t\t}\n\n\t\treturn string.replace( pattern, replace );\n\n\t}\n\n\tfunction WebGLProgram( renderer, code, material, parameters ) {\n\n\t\tvar gl = renderer.context;\n\n\t\tvar extensions = material.extensions;\n\t\tvar defines = material.defines;\n\n\t\tvar vertexShader = material.__webglShader.vertexShader;\n\t\tvar fragmentShader = material.__webglShader.fragmentShader;\n\n\t\tvar shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';\n\n\t\tif ( parameters.shadowMapType === PCFShadowMap ) {\n\n\t\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';\n\n\t\t} else if ( parameters.shadowMapType === PCFSoftShadowMap ) {\n\n\t\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';\n\n\t\t}\n\n\t\tvar envMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n\t\tvar envMapModeDefine = 'ENVMAP_MODE_REFLECTION';\n\t\tvar envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n\n\t\tif ( parameters.envMap ) {\n\n\t\t\tswitch ( material.envMap.mapping ) {\n\n\t\t\t\tcase CubeReflectionMapping:\n\t\t\t\tcase CubeRefractionMapping:\n\t\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CubeUVReflectionMapping:\n\t\t\t\tcase CubeUVRefractionMapping:\n\t\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase EquirectangularReflectionMapping:\n\t\t\t\tcase EquirectangularRefractionMapping:\n\t\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SphericalReflectionMapping:\n\t\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_SPHERE';\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tswitch ( material.envMap.mapping ) {\n\n\t\t\t\tcase CubeRefractionMapping:\n\t\t\t\tcase EquirectangularRefractionMapping:\n\t\t\t\t\tenvMapModeDefine = 'ENVMAP_MODE_REFRACTION';\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tswitch ( material.combine ) {\n\n\t\t\t\tcase MultiplyOperation:\n\t\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MixOperation:\n\t\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_MIX';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase AddOperation:\n\t\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_ADD';\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tvar gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;\n\n\t\t// console.log( 'building new program ' );\n\n\t\t//\n\n\t\tvar customExtensions = generateExtensions( extensions, parameters, renderer.extensions );\n\n\t\tvar customDefines = generateDefines( defines );\n\n\t\t//\n\n\t\tvar program = gl.createProgram();\n\n\t\tvar prefixVertex, prefixFragment;\n\n\t\tif ( material.isRawShaderMaterial ) {\n\n\t\t\tprefixVertex = [\n\n\t\t\t\tcustomDefines,\n\n\t\t\t\t'\\n'\n\n\t\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\t\tprefixFragment = [\n\n\t\t\t\tcustomExtensions,\n\t\t\t\tcustomDefines,\n\n\t\t\t\t'\\n'\n\n\t\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\t} else {\n\n\t\t\tprefixVertex = [\n\n\t\t\t\t'precision ' + parameters.precision + ' float;',\n\t\t\t\t'precision ' + parameters.precision + ' int;',\n\n\t\t\t\t'#define SHADER_NAME ' + material.__webglShader.name,\n\n\t\t\t\tcustomDefines,\n\n\t\t\t\tparameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',\n\n\t\t\t\t'#define GAMMA_FACTOR ' + gammaFactorDefine,\n\n\t\t\t\t'#define MAX_BONES ' + parameters.maxBones,\n\n\t\t\t\tparameters.map ? '#define USE_MAP' : '',\n\t\t\t\tparameters.envMap ? '#define USE_ENVMAP' : '',\n\t\t\t\tparameters.envMap ? '#define ' + envMapModeDefine : '',\n\t\t\t\tparameters.lightMap ? '#define USE_LIGHTMAP' : '',\n\t\t\t\tparameters.aoMap ? '#define USE_AOMAP' : '',\n\t\t\t\tparameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n\t\t\t\tparameters.bumpMap ? '#define USE_BUMPMAP' : '',\n\t\t\t\tparameters.normalMap ? '#define USE_NORMALMAP' : '',\n\t\t\t\tparameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',\n\t\t\t\tparameters.specularMap ? '#define USE_SPECULARMAP' : '',\n\t\t\t\tparameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n\t\t\t\tparameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n\t\t\t\tparameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n\t\t\t\tparameters.vertexColors ? '#define USE_COLOR' : '',\n\n\t\t\t\tparameters.flatShading ? '#define FLAT_SHADED' : '',\n\n\t\t\t\tparameters.skinning ? '#define USE_SKINNING' : '',\n\t\t\t\tparameters.useVertexTexture ? '#define BONE_TEXTURE' : '',\n\n\t\t\t\tparameters.morphTargets ? '#define USE_MORPHTARGETS' : '',\n\t\t\t\tparameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',\n\t\t\t\tparameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n\t\t\t\tparameters.flipSided ? '#define FLIP_SIDED' : '',\n\n\t\t\t\t'#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes,\n\n\t\t\t\tparameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n\t\t\t\tparameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n\t\t\t\tparameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',\n\n\t\t\t\tparameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n\t\t\t\tparameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n\t\t\t\t'uniform mat4 modelMatrix;',\n\t\t\t\t'uniform mat4 modelViewMatrix;',\n\t\t\t\t'uniform mat4 projectionMatrix;',\n\t\t\t\t'uniform mat4 viewMatrix;',\n\t\t\t\t'uniform mat3 normalMatrix;',\n\t\t\t\t'uniform vec3 cameraPosition;',\n\n\t\t\t\t'attribute vec3 position;',\n\t\t\t\t'attribute vec3 normal;',\n\t\t\t\t'attribute vec2 uv;',\n\n\t\t\t\t'#ifdef USE_COLOR',\n\n\t\t\t\t'\tattribute vec3 color;',\n\n\t\t\t\t'#endif',\n\n\t\t\t\t'#ifdef USE_MORPHTARGETS',\n\n\t\t\t\t'\tattribute vec3 morphTarget0;',\n\t\t\t\t'\tattribute vec3 morphTarget1;',\n\t\t\t\t'\tattribute vec3 morphTarget2;',\n\t\t\t\t'\tattribute vec3 morphTarget3;',\n\n\t\t\t\t'\t#ifdef USE_MORPHNORMALS',\n\n\t\t\t\t'\t\tattribute vec3 morphNormal0;',\n\t\t\t\t'\t\tattribute vec3 morphNormal1;',\n\t\t\t\t'\t\tattribute vec3 morphNormal2;',\n\t\t\t\t'\t\tattribute vec3 morphNormal3;',\n\n\t\t\t\t'\t#else',\n\n\t\t\t\t'\t\tattribute vec3 morphTarget4;',\n\t\t\t\t'\t\tattribute vec3 morphTarget5;',\n\t\t\t\t'\t\tattribute vec3 morphTarget6;',\n\t\t\t\t'\t\tattribute vec3 morphTarget7;',\n\n\t\t\t\t'\t#endif',\n\n\t\t\t\t'#endif',\n\n\t\t\t\t'#ifdef USE_SKINNING',\n\n\t\t\t\t'\tattribute vec4 skinIndex;',\n\t\t\t\t'\tattribute vec4 skinWeight;',\n\n\t\t\t\t'#endif',\n\n\t\t\t\t'\\n'\n\n\t\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\t\tprefixFragment = [\n\n\t\t\t\tcustomExtensions,\n\n\t\t\t\t'precision ' + parameters.precision + ' float;',\n\t\t\t\t'precision ' + parameters.precision + ' int;',\n\n\t\t\t\t'#define SHADER_NAME ' + material.__webglShader.name,\n\n\t\t\t\tcustomDefines,\n\n\t\t\t\tparameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',\n\n\t\t\t\t'#define GAMMA_FACTOR ' + gammaFactorDefine,\n\n\t\t\t\t( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',\n\t\t\t\t( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',\n\n\t\t\t\tparameters.map ? '#define USE_MAP' : '',\n\t\t\t\tparameters.envMap ? '#define USE_ENVMAP' : '',\n\t\t\t\tparameters.envMap ? '#define ' + envMapTypeDefine : '',\n\t\t\t\tparameters.envMap ? '#define ' + envMapModeDefine : '',\n\t\t\t\tparameters.envMap ? '#define ' + envMapBlendingDefine : '',\n\t\t\t\tparameters.lightMap ? '#define USE_LIGHTMAP' : '',\n\t\t\t\tparameters.aoMap ? '#define USE_AOMAP' : '',\n\t\t\t\tparameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n\t\t\t\tparameters.bumpMap ? '#define USE_BUMPMAP' : '',\n\t\t\t\tparameters.normalMap ? '#define USE_NORMALMAP' : '',\n\t\t\t\tparameters.specularMap ? '#define USE_SPECULARMAP' : '',\n\t\t\t\tparameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n\t\t\t\tparameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n\t\t\t\tparameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n\t\t\t\tparameters.vertexColors ? '#define USE_COLOR' : '',\n\n\t\t\t\tparameters.flatShading ? '#define FLAT_SHADED' : '',\n\n\t\t\t\tparameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n\t\t\t\tparameters.flipSided ? '#define FLIP_SIDED' : '',\n\n\t\t\t\t'#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes,\n\t\t\t\t'#define UNION_CLIPPING_PLANES ' + (parameters.numClippingPlanes - parameters.numClipIntersection),\n\n\t\t\t\tparameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n\t\t\t\tparameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n\t\t\t\tparameters.premultipliedAlpha ? \"#define PREMULTIPLIED_ALPHA\" : '',\n\n\t\t\t\tparameters.physicallyCorrectLights ? \"#define PHYSICALLY_CORRECT_LIGHTS\" : '',\n\n\t\t\t\tparameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n\t\t\t\tparameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n\t\t\t\tparameters.envMap && renderer.extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '',\n\n\t\t\t\t'uniform mat4 viewMatrix;',\n\t\t\t\t'uniform vec3 cameraPosition;',\n\n\t\t\t\t( parameters.toneMapping !== NoToneMapping ) ? \"#define TONE_MAPPING\" : '',\n\t\t\t\t( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below\n\t\t\t\t( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( \"toneMapping\", parameters.toneMapping ) : '',\n\n\t\t\t\t( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below\n\t\t\t\tparameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '',\n\t\t\t\tparameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '',\n\t\t\t\tparameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '',\n\t\t\t\tparameters.outputEncoding ? getTexelEncodingFunction( \"linearToOutputTexel\", parameters.outputEncoding ) : '',\n\n\t\t\t\tparameters.depthPacking ? \"#define DEPTH_PACKING \" + material.depthPacking : '',\n\n\t\t\t\t'\\n'\n\n\t\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\t}\n\n\t\tvertexShader = parseIncludes( vertexShader, parameters );\n\t\tvertexShader = replaceLightNums( vertexShader, parameters );\n\n\t\tfragmentShader = parseIncludes( fragmentShader, parameters );\n\t\tfragmentShader = replaceLightNums( fragmentShader, parameters );\n\n\t\tif ( ! material.isShaderMaterial ) {\n\n\t\t\tvertexShader = unrollLoops( vertexShader );\n\t\t\tfragmentShader = unrollLoops( fragmentShader );\n\n\t\t}\n\n\t\tvar vertexGlsl = prefixVertex + vertexShader;\n\t\tvar fragmentGlsl = prefixFragment + fragmentShader;\n\n\t\t// console.log( '*VERTEX*', vertexGlsl );\n\t\t// console.log( '*FRAGMENT*', fragmentGlsl );\n\n\t\tvar glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );\n\t\tvar glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );\n\n\t\tgl.attachShader( program, glVertexShader );\n\t\tgl.attachShader( program, glFragmentShader );\n\n\t\t// Force a particular attribute to index 0.\n\n\t\tif ( material.index0AttributeName !== undefined ) {\n\n\t\t\tgl.bindAttribLocation( program, 0, material.index0AttributeName );\n\n\t\t} else if ( parameters.morphTargets === true ) {\n\n\t\t\t// programs with morphTargets displace position out of attribute 0\n\t\t\tgl.bindAttribLocation( program, 0, 'position' );\n\n\t\t}\n\n\t\tgl.linkProgram( program );\n\n\t\tvar programLog = gl.getProgramInfoLog( program );\n\t\tvar vertexLog = gl.getShaderInfoLog( glVertexShader );\n\t\tvar fragmentLog = gl.getShaderInfoLog( glFragmentShader );\n\n\t\tvar runnable = true;\n\t\tvar haveDiagnostics = true;\n\n\t\t// console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) );\n\t\t// console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) );\n\n\t\tif ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {\n\n\t\t\trunnable = false;\n\n\t\t\tconsole.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog );\n\n\t\t} else if ( programLog !== '' ) {\n\n\t\t\tconsole.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );\n\n\t\t} else if ( vertexLog === '' || fragmentLog === '' ) {\n\n\t\t\thaveDiagnostics = false;\n\n\t\t}\n\n\t\tif ( haveDiagnostics ) {\n\n\t\t\tthis.diagnostics = {\n\n\t\t\t\trunnable: runnable,\n\t\t\t\tmaterial: material,\n\n\t\t\t\tprogramLog: programLog,\n\n\t\t\t\tvertexShader: {\n\n\t\t\t\t\tlog: vertexLog,\n\t\t\t\t\tprefix: prefixVertex\n\n\t\t\t\t},\n\n\t\t\t\tfragmentShader: {\n\n\t\t\t\t\tlog: fragmentLog,\n\t\t\t\t\tprefix: prefixFragment\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}\n\n\t\t// clean up\n\n\t\tgl.deleteShader( glVertexShader );\n\t\tgl.deleteShader( glFragmentShader );\n\n\t\t// set up caching for uniform locations\n\n\t\tvar cachedUniforms;\n\n\t\tthis.getUniforms = function() {\n\n\t\t\tif ( cachedUniforms === undefined ) {\n\n\t\t\t\tcachedUniforms =\n\t\t\t\t\t\tnew WebGLUniforms( gl, program, renderer );\n\n\t\t\t}\n\n\t\t\treturn cachedUniforms;\n\n\t\t};\n\n\t\t// set up caching for attribute locations\n\n\t\tvar cachedAttributes;\n\n\t\tthis.getAttributes = function() {\n\n\t\t\tif ( cachedAttributes === undefined ) {\n\n\t\t\t\tcachedAttributes = fetchAttributeLocations( gl, program );\n\n\t\t\t}\n\n\t\t\treturn cachedAttributes;\n\n\t\t};\n\n\t\t// free resource\n\n\t\tthis.destroy = function() {\n\n\t\t\tgl.deleteProgram( program );\n\t\t\tthis.program = undefined;\n\n\t\t};\n\n\t\t// DEPRECATED\n\n\t\tObject.defineProperties( this, {\n\n\t\t\tuniforms: {\n\t\t\t\tget: function() {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );\n\t\t\t\t\treturn this.getUniforms();\n\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tattributes: {\n\t\t\t\tget: function() {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );\n\t\t\t\t\treturn this.getAttributes();\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t} );\n\n\n\t\t//\n\n\t\tthis.id = programIdCount ++;\n\t\tthis.code = code;\n\t\tthis.usedTimes = 1;\n\t\tthis.program = program;\n\t\tthis.vertexShader = glVertexShader;\n\t\tthis.fragmentShader = glFragmentShader;\n\n\t\treturn this;\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLPrograms( renderer, capabilities ) {\n\n\t\tvar programs = [];\n\n\t\tvar shaderIDs = {\n\t\t\tMeshDepthMaterial: 'depth',\n\t\t\tMeshNormalMaterial: 'normal',\n\t\t\tMeshBasicMaterial: 'basic',\n\t\t\tMeshLambertMaterial: 'lambert',\n\t\t\tMeshPhongMaterial: 'phong',\n\t\t\tMeshStandardMaterial: 'physical',\n\t\t\tMeshPhysicalMaterial: 'physical',\n\t\t\tLineBasicMaterial: 'basic',\n\t\t\tLineDashedMaterial: 'dashed',\n\t\t\tPointsMaterial: 'points'\n\t\t};\n\n\t\tvar parameterNames = [\n\t\t\t\"precision\", \"supportsVertexTextures\", \"map\", \"mapEncoding\", \"envMap\", \"envMapMode\", \"envMapEncoding\",\n\t\t\t\"lightMap\", \"aoMap\", \"emissiveMap\", \"emissiveMapEncoding\", \"bumpMap\", \"normalMap\", \"displacementMap\", \"specularMap\",\n\t\t\t\"roughnessMap\", \"metalnessMap\",\n\t\t\t\"alphaMap\", \"combine\", \"vertexColors\", \"fog\", \"useFog\", \"fogExp\",\n\t\t\t\"flatShading\", \"sizeAttenuation\", \"logarithmicDepthBuffer\", \"skinning\",\n\t\t\t\"maxBones\", \"useVertexTexture\", \"morphTargets\", \"morphNormals\",\n\t\t\t\"maxMorphTargets\", \"maxMorphNormals\", \"premultipliedAlpha\",\n\t\t\t\"numDirLights\", \"numPointLights\", \"numSpotLights\", \"numHemiLights\",\n\t\t\t\"shadowMapEnabled\", \"shadowMapType\", \"toneMapping\", 'physicallyCorrectLights',\n\t\t\t\"alphaTest\", \"doubleSided\", \"flipSided\", \"numClippingPlanes\", \"numClipIntersection\", \"depthPacking\"\n\t\t];\n\n\n\t\tfunction allocateBones( object ) {\n\n\t\t\tif ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {\n\n\t\t\t\treturn 1024;\n\n\t\t\t} else {\n\n\t\t\t\t// default for when object is not specified\n\t\t\t\t// ( for example when prebuilding shader to be used with multiple objects )\n\t\t\t\t//\n\t\t\t\t// - leave some extra space for other uniforms\n\t\t\t\t// - limit here is ANGLE's 254 max uniform vectors\n\t\t\t\t// (up to 54 should be safe)\n\n\t\t\t\tvar nVertexUniforms = capabilities.maxVertexUniforms;\n\t\t\t\tvar nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );\n\n\t\t\t\tvar maxBones = nVertexMatrices;\n\n\t\t\t\tif ( object !== undefined && (object && object.isSkinnedMesh) ) {\n\n\t\t\t\t\tmaxBones = Math.min( object.skeleton.bones.length, maxBones );\n\n\t\t\t\t\tif ( maxBones < object.skeleton.bones.length ) {\n\n\t\t\t\t\t\tconsole.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn maxBones;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction getTextureEncodingFromMap( map, gammaOverrideLinear ) {\n\n\t\t\tvar encoding;\n\n\t\t\tif ( ! map ) {\n\n\t\t\t\tencoding = LinearEncoding;\n\n\t\t\t} else if ( (map && map.isTexture) ) {\n\n\t\t\t\tencoding = map.encoding;\n\n\t\t\t} else if ( (map && map.isWebGLRenderTarget) ) {\n\n\t\t\t\tconsole.warn( \"THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead.\" );\n\t\t\t\tencoding = map.texture.encoding;\n\n\t\t\t}\n\n\t\t\t// add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point.\n\t\t\tif ( encoding === LinearEncoding && gammaOverrideLinear ) {\n\n\t\t\t\tencoding = GammaEncoding;\n\n\t\t\t}\n\n\t\t\treturn encoding;\n\n\t\t}\n\n\t\tthis.getParameters = function ( material, lights, fog, nClipPlanes, nClipIntersection, object ) {\n\n\t\t\tvar shaderID = shaderIDs[ material.type ];\n\n\t\t\t// heuristics to create shader parameters according to lights in the scene\n\t\t\t// (not to blow over maxLights budget)\n\n\t\t\tvar maxBones = allocateBones( object );\n\t\t\tvar precision = renderer.getPrecision();\n\n\t\t\tif ( material.precision !== null ) {\n\n\t\t\t\tprecision = capabilities.getMaxPrecision( material.precision );\n\n\t\t\t\tif ( precision !== material.precision ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar currentRenderTarget = renderer.getCurrentRenderTarget();\n\n\t\t\tvar parameters = {\n\n\t\t\t\tshaderID: shaderID,\n\n\t\t\t\tprecision: precision,\n\t\t\t\tsupportsVertexTextures: capabilities.vertexTextures,\n\t\t\t\toutputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ),\n\t\t\t\tmap: !! material.map,\n\t\t\t\tmapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ),\n\t\t\t\tenvMap: !! material.envMap,\n\t\t\t\tenvMapMode: material.envMap && material.envMap.mapping,\n\t\t\t\tenvMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ),\n\t\t\t\tenvMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ),\n\t\t\t\tlightMap: !! material.lightMap,\n\t\t\t\taoMap: !! material.aoMap,\n\t\t\t\temissiveMap: !! material.emissiveMap,\n\t\t\t\temissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ),\n\t\t\t\tbumpMap: !! material.bumpMap,\n\t\t\t\tnormalMap: !! material.normalMap,\n\t\t\t\tdisplacementMap: !! material.displacementMap,\n\t\t\t\troughnessMap: !! material.roughnessMap,\n\t\t\t\tmetalnessMap: !! material.metalnessMap,\n\t\t\t\tspecularMap: !! material.specularMap,\n\t\t\t\talphaMap: !! material.alphaMap,\n\n\t\t\t\tcombine: material.combine,\n\n\t\t\t\tvertexColors: material.vertexColors,\n\n\t\t\t\tfog: !! fog,\n\t\t\t\tuseFog: material.fog,\n\t\t\t\tfogExp: (fog && fog.isFogExp2),\n\n\t\t\t\tflatShading: material.shading === FlatShading,\n\n\t\t\t\tsizeAttenuation: material.sizeAttenuation,\n\t\t\t\tlogarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,\n\n\t\t\t\tskinning: material.skinning,\n\t\t\t\tmaxBones: maxBones,\n\t\t\t\tuseVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture,\n\n\t\t\t\tmorphTargets: material.morphTargets,\n\t\t\t\tmorphNormals: material.morphNormals,\n\t\t\t\tmaxMorphTargets: renderer.maxMorphTargets,\n\t\t\t\tmaxMorphNormals: renderer.maxMorphNormals,\n\n\t\t\t\tnumDirLights: lights.directional.length,\n\t\t\t\tnumPointLights: lights.point.length,\n\t\t\t\tnumSpotLights: lights.spot.length,\n\t\t\t\tnumHemiLights: lights.hemi.length,\n\n\t\t\t\tnumClippingPlanes: nClipPlanes,\n\t\t\t\tnumClipIntersection: nClipIntersection,\n\n\t\t\t\tshadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0,\n\t\t\t\tshadowMapType: renderer.shadowMap.type,\n\n\t\t\t\ttoneMapping: renderer.toneMapping,\n\t\t\t\tphysicallyCorrectLights: renderer.physicallyCorrectLights,\n\n\t\t\t\tpremultipliedAlpha: material.premultipliedAlpha,\n\n\t\t\t\talphaTest: material.alphaTest,\n\t\t\t\tdoubleSided: material.side === DoubleSide,\n\t\t\t\tflipSided: material.side === BackSide,\n\n\t\t\t\tdepthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false\n\n\t\t\t};\n\n\t\t\treturn parameters;\n\n\t\t};\n\n\t\tthis.getProgramCode = function ( material, parameters ) {\n\n\t\t\tvar array = [];\n\n\t\t\tif ( parameters.shaderID ) {\n\n\t\t\t\tarray.push( parameters.shaderID );\n\n\t\t\t} else {\n\n\t\t\t\tarray.push( material.fragmentShader );\n\t\t\t\tarray.push( material.vertexShader );\n\n\t\t\t}\n\n\t\t\tif ( material.defines !== undefined ) {\n\n\t\t\t\tfor ( var name in material.defines ) {\n\n\t\t\t\t\tarray.push( name );\n\t\t\t\t\tarray.push( material.defines[ name ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfor ( var i = 0; i < parameterNames.length; i ++ ) {\n\n\t\t\t\tarray.push( parameters[ parameterNames[ i ] ] );\n\n\t\t\t}\n\n\t\t\treturn array.join();\n\n\t\t};\n\n\t\tthis.acquireProgram = function ( material, parameters, code ) {\n\n\t\t\tvar program;\n\n\t\t\t// Check if code has been already compiled\n\t\t\tfor ( var p = 0, pl = programs.length; p < pl; p ++ ) {\n\n\t\t\t\tvar programInfo = programs[ p ];\n\n\t\t\t\tif ( programInfo.code === code ) {\n\n\t\t\t\t\tprogram = programInfo;\n\t\t\t\t\t++ program.usedTimes;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( program === undefined ) {\n\n\t\t\t\tprogram = new WebGLProgram( renderer, code, material, parameters );\n\t\t\t\tprograms.push( program );\n\n\t\t\t}\n\n\t\t\treturn program;\n\n\t\t};\n\n\t\tthis.releaseProgram = function( program ) {\n\n\t\t\tif ( -- program.usedTimes === 0 ) {\n\n\t\t\t\t// Remove from unordered set\n\t\t\t\tvar i = programs.indexOf( program );\n\t\t\t\tprograms[ i ] = programs[ programs.length - 1 ];\n\t\t\t\tprograms.pop();\n\n\t\t\t\t// Free WebGL resources\n\t\t\t\tprogram.destroy();\n\n\t\t\t}\n\n\t\t};\n\n\t\t// Exposed for resource monitoring & error feedback via renderer.info:\n\t\tthis.programs = programs;\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLGeometries( gl, properties, info ) {\n\n\t\tvar geometries = {};\n\n\t\tfunction onGeometryDispose( event ) {\n\n\t\t\tvar geometry = event.target;\n\t\t\tvar buffergeometry = geometries[ geometry.id ];\n\n\t\t\tif ( buffergeometry.index !== null ) {\n\n\t\t\t\tdeleteAttribute( buffergeometry.index );\n\n\t\t\t}\n\n\t\t\tdeleteAttributes( buffergeometry.attributes );\n\n\t\t\tgeometry.removeEventListener( 'dispose', onGeometryDispose );\n\n\t\t\tdelete geometries[ geometry.id ];\n\n\t\t\t// TODO\n\n\t\t\tvar property = properties.get( geometry );\n\n\t\t\tif ( property.wireframe ) {\n\n\t\t\t\tdeleteAttribute( property.wireframe );\n\n\t\t\t}\n\n\t\t\tproperties.delete( geometry );\n\n\t\t\tvar bufferproperty = properties.get( buffergeometry );\n\n\t\t\tif ( bufferproperty.wireframe ) {\n\n\t\t\t\tdeleteAttribute( bufferproperty.wireframe );\n\n\t\t\t}\n\n\t\t\tproperties.delete( buffergeometry );\n\n\t\t\t//\n\n\t\t\tinfo.memory.geometries --;\n\n\t\t}\n\n\t\tfunction getAttributeBuffer( attribute ) {\n\n\t\t\tif ( attribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\treturn properties.get( attribute.data ).__webglBuffer;\n\n\t\t\t}\n\n\t\t\treturn properties.get( attribute ).__webglBuffer;\n\n\t\t}\n\n\t\tfunction deleteAttribute( attribute ) {\n\n\t\t\tvar buffer = getAttributeBuffer( attribute );\n\n\t\t\tif ( buffer !== undefined ) {\n\n\t\t\t\tgl.deleteBuffer( buffer );\n\t\t\t\tremoveAttributeBuffer( attribute );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction deleteAttributes( attributes ) {\n\n\t\t\tfor ( var name in attributes ) {\n\n\t\t\t\tdeleteAttribute( attributes[ name ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction removeAttributeBuffer( attribute ) {\n\n\t\t\tif ( attribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\tproperties.delete( attribute.data );\n\n\t\t\t} else {\n\n\t\t\t\tproperties.delete( attribute );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn {\n\n\t\t\tget: function ( object ) {\n\n\t\t\t\tvar geometry = object.geometry;\n\n\t\t\t\tif ( geometries[ geometry.id ] !== undefined ) {\n\n\t\t\t\t\treturn geometries[ geometry.id ];\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.addEventListener( 'dispose', onGeometryDispose );\n\n\t\t\t\tvar buffergeometry;\n\n\t\t\t\tif ( geometry.isBufferGeometry ) {\n\n\t\t\t\t\tbuffergeometry = geometry;\n\n\t\t\t\t} else if ( geometry.isGeometry ) {\n\n\t\t\t\t\tif ( geometry._bufferGeometry === undefined ) {\n\n\t\t\t\t\t\tgeometry._bufferGeometry = new BufferGeometry().setFromObject( object );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbuffergeometry = geometry._bufferGeometry;\n\n\t\t\t\t}\n\n\t\t\t\tgeometries[ geometry.id ] = buffergeometry;\n\n\t\t\t\tinfo.memory.geometries ++;\n\n\t\t\t\treturn buffergeometry;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLObjects( gl, properties, info ) {\n\n\t\tvar geometries = new WebGLGeometries( gl, properties, info );\n\n\t\t//\n\n\t\tfunction update( object ) {\n\n\t\t\t// TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter.\n\n\t\t\tvar geometry = geometries.get( object );\n\n\t\t\tif ( object.geometry.isGeometry ) {\n\n\t\t\t\tgeometry.updateFromObject( object );\n\n\t\t\t}\n\n\t\t\tvar index = geometry.index;\n\t\t\tvar attributes = geometry.attributes;\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tupdateAttribute( index, gl.ELEMENT_ARRAY_BUFFER );\n\n\t\t\t}\n\n\t\t\tfor ( var name in attributes ) {\n\n\t\t\t\tupdateAttribute( attributes[ name ], gl.ARRAY_BUFFER );\n\n\t\t\t}\n\n\t\t\t// morph targets\n\n\t\t\tvar morphAttributes = geometry.morphAttributes;\n\n\t\t\tfor ( var name in morphAttributes ) {\n\n\t\t\t\tvar array = morphAttributes[ name ];\n\n\t\t\t\tfor ( var i = 0, l = array.length; i < l; i ++ ) {\n\n\t\t\t\t\tupdateAttribute( array[ i ], gl.ARRAY_BUFFER );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn geometry;\n\n\t\t}\n\n\t\tfunction updateAttribute( attribute, bufferType ) {\n\n\t\t\tvar data = ( attribute.isInterleavedBufferAttribute ) ? attribute.data : attribute;\n\n\t\t\tvar attributeProperties = properties.get( data );\n\n\t\t\tif ( attributeProperties.__webglBuffer === undefined ) {\n\n\t\t\t\tcreateBuffer( attributeProperties, data, bufferType );\n\n\t\t\t} else if ( attributeProperties.version !== data.version ) {\n\n\t\t\t\tupdateBuffer( attributeProperties, data, bufferType );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction createBuffer( attributeProperties, data, bufferType ) {\n\n\t\t\tattributeProperties.__webglBuffer = gl.createBuffer();\n\t\t\tgl.bindBuffer( bufferType, attributeProperties.__webglBuffer );\n\n\t\t\tvar usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;\n\n\t\t\tgl.bufferData( bufferType, data.array, usage );\n\n\t\t\tattributeProperties.version = data.version;\n\n\t\t}\n\n\t\tfunction updateBuffer( attributeProperties, data, bufferType ) {\n\n\t\t\tgl.bindBuffer( bufferType, attributeProperties.__webglBuffer );\n\n\t\t\tif ( data.dynamic === false ) {\n\n\t\t\t\tgl.bufferData( bufferType, data.array, gl.STATIC_DRAW );\n\n\t\t\t} else if ( data.updateRange.count === - 1 ) {\n\n\t\t\t\t// Not using update ranges\n\n\t\t\t\tgl.bufferSubData( bufferType, 0, data.array );\n\n\t\t\t} else if ( data.updateRange.count === 0 ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );\n\n\t\t\t} else {\n\n\t\t\t\tgl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT,\n\t\t\t\t\t\t\t\t data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) );\n\n\t\t\t\tdata.updateRange.count = 0; // reset range\n\n\t\t\t}\n\n\t\t\tattributeProperties.version = data.version;\n\n\t\t}\n\n\t\tfunction getAttributeBuffer( attribute ) {\n\n\t\t\tif ( attribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\treturn properties.get( attribute.data ).__webglBuffer;\n\n\t\t\t}\n\n\t\t\treturn properties.get( attribute ).__webglBuffer;\n\n\t\t}\n\n\t\tfunction getWireframeAttribute( geometry ) {\n\n\t\t\tvar property = properties.get( geometry );\n\n\t\t\tif ( property.wireframe !== undefined ) {\n\n\t\t\t\treturn property.wireframe;\n\n\t\t\t}\n\n\t\t\tvar indices = [];\n\n\t\t\tvar index = geometry.index;\n\t\t\tvar attributes = geometry.attributes;\n\t\t\tvar position = attributes.position;\n\n\t\t\t// console.time( 'wireframe' );\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tvar edges = {};\n\t\t\t\tvar array = index.array;\n\n\t\t\t\tfor ( var i = 0, l = array.length; i < l; i += 3 ) {\n\n\t\t\t\t\tvar a = array[ i + 0 ];\n\t\t\t\t\tvar b = array[ i + 1 ];\n\t\t\t\t\tvar c = array[ i + 2 ];\n\n\t\t\t\t\tindices.push( a, b, b, c, c, a );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tvar array = attributes.position.array;\n\n\t\t\t\tfor ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {\n\n\t\t\t\t\tvar a = i + 0;\n\t\t\t\t\tvar b = i + 1;\n\t\t\t\t\tvar c = i + 2;\n\n\t\t\t\t\tindices.push( a, b, b, c, c, a );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// console.timeEnd( 'wireframe' );\n\n\t\t\tvar TypeArray = position.count > 65535 ? Uint32Array : Uint16Array;\n\t\t\tvar attribute = new BufferAttribute( new TypeArray( indices ), 1 );\n\n\t\t\tupdateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER );\n\n\t\t\tproperty.wireframe = attribute;\n\n\t\t\treturn attribute;\n\n\t\t}\n\n\t\treturn {\n\n\t\t\tgetAttributeBuffer: getAttributeBuffer,\n\t\t\tgetWireframeAttribute: getWireframeAttribute,\n\n\t\t\tupdate: update\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLTextures( _gl, extensions, state, properties, capabilities, paramThreeToGL, info ) {\n\n\t\tvar _infoMemory = info.memory;\n\t\tvar _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext );\n\n\t\t//\n\n\t\tfunction clampToMaxSize( image, maxSize ) {\n\n\t\t\tif ( image.width > maxSize || image.height > maxSize ) {\n\n\t\t\t\t// Warning: Scaling through the canvas will only work with images that use\n\t\t\t\t// premultiplied alpha.\n\n\t\t\t\tvar scale = maxSize / Math.max( image.width, image.height );\n\n\t\t\t\tvar canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n\t\t\t\tcanvas.width = Math.floor( image.width * scale );\n\t\t\t\tcanvas.height = Math.floor( image.height * scale );\n\n\t\t\t\tvar context = canvas.getContext( '2d' );\n\t\t\t\tcontext.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );\n\n\t\t\t\treturn canvas;\n\n\t\t\t}\n\n\t\t\treturn image;\n\n\t\t}\n\n\t\tfunction isPowerOfTwo( image ) {\n\n\t\t\treturn _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height );\n\n\t\t}\n\n\t\tfunction makePowerOfTwo( image ) {\n\n\t\t\tif ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) {\n\n\t\t\t\tvar canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n\t\t\t\tcanvas.width = _Math.nearestPowerOfTwo( image.width );\n\t\t\t\tcanvas.height = _Math.nearestPowerOfTwo( image.height );\n\n\t\t\t\tvar context = canvas.getContext( '2d' );\n\t\t\t\tcontext.drawImage( image, 0, 0, canvas.width, canvas.height );\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );\n\n\t\t\t\treturn canvas;\n\n\t\t\t}\n\n\t\t\treturn image;\n\n\t\t}\n\n\t\tfunction textureNeedsPowerOfTwo( texture ) {\n\n\t\t\tif ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) return true;\n\t\t\tif ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) return true;\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\t// Fallback filters for non-power-of-2 textures\n\n\t\tfunction filterFallback( f ) {\n\n\t\t\tif ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) {\n\n\t\t\t\treturn _gl.NEAREST;\n\n\t\t\t}\n\n\t\t\treturn _gl.LINEAR;\n\n\t\t}\n\n\t\t//\n\n\t\tfunction onTextureDispose( event ) {\n\n\t\t\tvar texture = event.target;\n\n\t\t\ttexture.removeEventListener( 'dispose', onTextureDispose );\n\n\t\t\tdeallocateTexture( texture );\n\n\t\t\t_infoMemory.textures --;\n\n\n\t\t}\n\n\t\tfunction onRenderTargetDispose( event ) {\n\n\t\t\tvar renderTarget = event.target;\n\n\t\t\trenderTarget.removeEventListener( 'dispose', onRenderTargetDispose );\n\n\t\t\tdeallocateRenderTarget( renderTarget );\n\n\t\t\t_infoMemory.textures --;\n\n\t\t}\n\n\t\t//\n\n\t\tfunction deallocateTexture( texture ) {\n\n\t\t\tvar textureProperties = properties.get( texture );\n\n\t\t\tif ( texture.image && textureProperties.__image__webglTextureCube ) {\n\n\t\t\t\t// cube texture\n\n\t\t\t\t_gl.deleteTexture( textureProperties.__image__webglTextureCube );\n\n\t\t\t} else {\n\n\t\t\t\t// 2D texture\n\n\t\t\t\tif ( textureProperties.__webglInit === undefined ) return;\n\n\t\t\t\t_gl.deleteTexture( textureProperties.__webglTexture );\n\n\t\t\t}\n\n\t\t\t// remove all webgl properties\n\t\t\tproperties.delete( texture );\n\n\t\t}\n\n\t\tfunction deallocateRenderTarget( renderTarget ) {\n\n\t\t\tvar renderTargetProperties = properties.get( renderTarget );\n\t\t\tvar textureProperties = properties.get( renderTarget.texture );\n\n\t\t\tif ( ! renderTarget ) return;\n\n\t\t\tif ( textureProperties.__webglTexture !== undefined ) {\n\n\t\t\t\t_gl.deleteTexture( textureProperties.__webglTexture );\n\n\t\t\t}\n\n\t\t\tif ( renderTarget.depthTexture ) {\n\n\t\t\t\trenderTarget.depthTexture.dispose();\n\n\t\t\t}\n\n\t\t\tif ( (renderTarget && renderTarget.isWebGLRenderTargetCube) ) {\n\n\t\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\t_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );\n\t\t\t\t\tif ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );\n\t\t\t\tif ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );\n\n\t\t\t}\n\n\t\t\tproperties.delete( renderTarget.texture );\n\t\t\tproperties.delete( renderTarget );\n\n\t\t}\n\n\t\t//\n\n\n\n\t\tfunction setTexture2D( texture, slot ) {\n\n\t\t\tvar textureProperties = properties.get( texture );\n\n\t\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\t\tvar image = texture.image;\n\n\t\t\t\tif ( image === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture );\n\n\t\t\t\t} else if ( image.complete === false ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tuploadTexture( textureProperties, texture, slot );\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\t\t\tstate.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n\n\t\t}\n\n\t\tfunction setTextureCube( texture, slot ) {\n\n\t\t\tvar textureProperties = properties.get( texture );\n\n\t\t\tif ( texture.image.length === 6 ) {\n\n\t\t\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\t\t\tif ( ! textureProperties.__image__webglTextureCube ) {\n\n\t\t\t\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t\t\t\t\ttextureProperties.__image__webglTextureCube = _gl.createTexture();\n\n\t\t\t\t\t\t_infoMemory.textures ++;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\t\t\t\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );\n\n\t\t\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\n\t\t\t\t\tvar isCompressed = (texture && texture.isCompressedTexture);\n\t\t\t\t\tvar isDataTexture = (texture.image[ 0 ] && texture.image[ 0 ].isDataTexture);\n\n\t\t\t\t\tvar cubeImage = [];\n\n\t\t\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\t\tif ( ! isCompressed && ! isDataTexture ) {\n\n\t\t\t\t\t\t\tcubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tcubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tvar image = cubeImage[ 0 ],\n\t\t\t\t\tisPowerOfTwoImage = isPowerOfTwo( image ),\n\t\t\t\t\tglFormat = paramThreeToGL( texture.format ),\n\t\t\t\t\tglType = paramThreeToGL( texture.type );\n\n\t\t\t\t\tsetTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage );\n\n\t\t\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\t\tif ( ! isCompressed ) {\n\n\t\t\t\t\t\t\tif ( isDataTexture ) {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tvar mipmap, mipmaps = cubeImage[ i ].mipmaps;\n\n\t\t\t\t\t\t\tfor ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {\n\n\t\t\t\t\t\t\t\tmipmap = mipmaps[ j ];\n\n\t\t\t\t\t\t\t\tif ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {\n\n\t\t\t\t\t\t\t\t\tif ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {\n\n\t\t\t\t\t\t\t\t\t\tstate.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\tconsole.warn( \"THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()\" );\n\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( texture.generateMipmaps && isPowerOfTwoImage ) {\n\n\t\t\t\t\t\t_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttextureProperties.__version = texture.version;\n\n\t\t\t\t\tif ( texture.onUpdate ) texture.onUpdate( texture );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\t\t\t\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction setTextureCubeDynamic( texture, slot ) {\n\n\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\t\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture );\n\n\t\t}\n\n\t\tfunction setTextureParameters( textureType, texture, isPowerOfTwoImage ) {\n\n\t\t\tvar extension;\n\n\t\t\tif ( isPowerOfTwoImage ) {\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );\n\n\t\t\t\tif ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture );\n\n\t\t\t\t}\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );\n\n\t\t\t\tif ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\textension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n\t\t\tif ( extension ) {\n\n\t\t\t\tif ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;\n\t\t\t\tif ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return;\n\n\t\t\t\tif ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {\n\n\t\t\t\t\t_gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );\n\t\t\t\t\tproperties.get( texture ).__currentAnisotropy = texture.anisotropy;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction uploadTexture( textureProperties, texture, slot ) {\n\n\t\t\tif ( textureProperties.__webglInit === undefined ) {\n\n\t\t\t\ttextureProperties.__webglInit = true;\n\n\t\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t\t\ttextureProperties.__webglTexture = _gl.createTexture();\n\n\t\t\t\t_infoMemory.textures ++;\n\n\t\t\t}\n\n\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\t\t\tstate.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );\n\n\t\t\tvar image = clampToMaxSize( texture.image, capabilities.maxTextureSize );\n\n\t\t\tif ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) {\n\n\t\t\t\timage = makePowerOfTwo( image );\n\n\t\t\t}\n\n\t\t\tvar isPowerOfTwoImage = isPowerOfTwo( image ),\n\t\t\tglFormat = paramThreeToGL( texture.format ),\n\t\t\tglType = paramThreeToGL( texture.type );\n\n\t\t\tsetTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage );\n\n\t\t\tvar mipmap, mipmaps = texture.mipmaps;\n\n\t\t\tif ( (texture && texture.isDepthTexture) ) {\n\n\t\t\t\t// populate depth texture with dummy data\n\n\t\t\t\tvar internalFormat = _gl.DEPTH_COMPONENT;\n\n\t\t\t\tif ( texture.type === FloatType ) {\n\n\t\t\t\t\tif ( !_isWebGL2 ) throw new Error('Float Depth Texture only supported in WebGL2.0');\n\t\t\t\t\tinternalFormat = _gl.DEPTH_COMPONENT32F;\n\n\t\t\t\t} else if ( _isWebGL2 ) {\n\n\t\t\t\t\t// WebGL 2.0 requires signed internalformat for glTexImage2D\n\t\t\t\t\tinternalFormat = _gl.DEPTH_COMPONENT16;\n\n\t\t\t\t}\n\n\t\t\t\t// Depth stencil textures need the DEPTH_STENCIL internal format\n\t\t\t\t// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n\t\t\t\tif ( texture.format === DepthStencilFormat ) {\n\n\t\t\t\t\tinternalFormat = _gl.DEPTH_STENCIL;\n\n\t\t\t\t}\n\n\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null );\n\n\t\t\t} else if ( (texture && texture.isDataTexture) ) {\n\n\t\t\t\t// use manually created mipmaps if available\n\t\t\t\t// if there are no manual mipmaps\n\t\t\t\t// set 0 level mipmap and then use GL to generate other mipmap levels\n\n\t\t\t\tif ( mipmaps.length > 0 && isPowerOfTwoImage ) {\n\n\t\t\t\t\tfor ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );\n\n\t\t\t\t}\n\n\t\t\t} else if ( (texture && texture.isCompressedTexture) ) {\n\n\t\t\t\tfor ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\tif ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {\n\n\t\t\t\t\t\tif ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {\n\n\t\t\t\t\t\t\tstate.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tconsole.warn( \"THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()\" );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// regular Texture (image, video, canvas)\n\n\t\t\t\t// use manually created mipmaps if available\n\t\t\t\t// if there are no manual mipmaps\n\t\t\t\t// set 0 level mipmap and then use GL to generate other mipmap levels\n\n\t\t\t\tif ( mipmaps.length > 0 && isPowerOfTwoImage ) {\n\n\t\t\t\t\tfor ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( texture.generateMipmaps && isPowerOfTwoImage ) _gl.generateMipmap( _gl.TEXTURE_2D );\n\n\t\t\ttextureProperties.__version = texture.version;\n\n\t\t\tif ( texture.onUpdate ) texture.onUpdate( texture );\n\n\t\t}\n\n\t\t// Render targets\n\n\t\t// Setup storage for target texture and bind it to correct framebuffer\n\t\tfunction setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) {\n\n\t\t\tvar glFormat = paramThreeToGL( renderTarget.texture.format );\n\t\t\tvar glType = paramThreeToGL( renderTarget.texture.type );\n\t\t\tstate.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );\n\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );\n\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t\t}\n\n\t\t// Setup storage for internal depth/stencil buffers and bind to correct framebuffer\n\t\tfunction setupRenderBufferStorage( renderbuffer, renderTarget ) {\n\n\t\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );\n\n\t\t\tif ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {\n\n\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );\n\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n\t\t\t} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {\n\n\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );\n\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n\t\t\t} else {\n\n\t\t\t\t// FIXME: We don't support !depth !stencil\n\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );\n\n\t\t\t}\n\n\t\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );\n\n\t\t}\n\n\t\t// Setup resources for a Depth Texture for a FBO (needs an extension)\n\t\tfunction setupDepthTexture( framebuffer, renderTarget ) {\n\n\t\t\tvar isCube = ( (renderTarget && renderTarget.isWebGLRenderTargetCube) );\n\t\t\tif ( isCube ) throw new Error('Depth Texture with cube render targets is not supported!');\n\n\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\tif ( !( (renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture) ) ) {\n\n\t\t\t\tthrow new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture');\n\n\t\t\t}\n\n\t\t\t// upload an empty depth texture with framebuffer size\n\t\t\tif ( !properties.get( renderTarget.depthTexture ).__webglTexture ||\n\t\t\t\t\trenderTarget.depthTexture.image.width !== renderTarget.width ||\n\t\t\t\t\trenderTarget.depthTexture.image.height !== renderTarget.height ) {\n\t\t\t\trenderTarget.depthTexture.image.width = renderTarget.width;\n\t\t\t\trenderTarget.depthTexture.image.height = renderTarget.height;\n\t\t\t\trenderTarget.depthTexture.needsUpdate = true;\n\t\t\t}\n\n\t\t\tsetTexture2D( renderTarget.depthTexture, 0 );\n\n\t\t\tvar webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;\n\n\t\t\tif ( renderTarget.depthTexture.format === DepthFormat ) {\n\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n\t\t\t} else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {\n\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n\t\t\t} else {\n\n\t\t\t\tthrow new Error('Unknown depthTexture format')\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Setup GL resources for a non-texture depth buffer\n\t\tfunction setupDepthRenderbuffer( renderTarget ) {\n\n\t\t\tvar renderTargetProperties = properties.get( renderTarget );\n\n\t\t\tvar isCube = ( (renderTarget && renderTarget.isWebGLRenderTargetCube) );\n\n\t\t\tif ( renderTarget.depthTexture ) {\n\n\t\t\t\tif ( isCube ) throw new Error('target.depthTexture not supported in Cube render targets');\n\n\t\t\t\tsetupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );\n\n\t\t\t} else {\n\n\t\t\t\tif ( isCube ) {\n\n\t\t\t\t\trenderTargetProperties.__webglDepthbuffer = [];\n\n\t\t\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );\n\t\t\t\t\t\trenderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();\n\t\t\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\t\t\t\t\trenderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();\n\t\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t\t}\n\n\t\t// Set up GL resources for the render target\n\t\tfunction setupRenderTarget( renderTarget ) {\n\n\t\t\tvar renderTargetProperties = properties.get( renderTarget );\n\t\t\tvar textureProperties = properties.get( renderTarget.texture );\n\n\t\t\trenderTarget.addEventListener( 'dispose', onRenderTargetDispose );\n\n\t\t\ttextureProperties.__webglTexture = _gl.createTexture();\n\n\t\t\t_infoMemory.textures ++;\n\n\t\t\tvar isCube = ( (renderTarget && renderTarget.isWebGLRenderTargetCube) );\n\t\t\tvar isTargetPowerOfTwo = isPowerOfTwo( renderTarget );\n\n\t\t\t// Setup framebuffer\n\n\t\t\tif ( isCube ) {\n\n\t\t\t\trenderTargetProperties.__webglFramebuffer = [];\n\n\t\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\trenderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();\n\n\t\t\t}\n\n\t\t\t// Setup color buffer\n\n\t\t\tif ( isCube ) {\n\n\t\t\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );\n\t\t\t\tsetTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo );\n\n\t\t\t\tfor ( var i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );\n\n\t\t\t\t}\n\n\t\t\t\tif ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );\n\t\t\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, null );\n\n\t\t\t} else {\n\n\t\t\t\tstate.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n\t\t\t\tsetTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo );\n\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D );\n\n\t\t\t\tif ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );\n\t\t\t\tstate.bindTexture( _gl.TEXTURE_2D, null );\n\n\t\t\t}\n\n\t\t\t// Setup depth and stencil buffers\n\n\t\t\tif ( renderTarget.depthBuffer ) {\n\n\t\t\t\tsetupDepthRenderbuffer( renderTarget );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction updateRenderTargetMipmap( renderTarget ) {\n\n\t\t\tvar texture = renderTarget.texture;\n\n\t\t\tif ( texture.generateMipmaps && isPowerOfTwo( renderTarget ) &&\n\t\t\t\t\ttexture.minFilter !== NearestFilter &&\n\t\t\t\t\ttexture.minFilter !== LinearFilter ) {\n\n\t\t\t\tvar target = (renderTarget && renderTarget.isWebGLRenderTargetCube) ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;\n\t\t\t\tvar webglTexture = properties.get( texture ).__webglTexture;\n\n\t\t\t\tstate.bindTexture( target, webglTexture );\n\t\t\t\t_gl.generateMipmap( target );\n\t\t\t\tstate.bindTexture( target, null );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.setTexture2D = setTexture2D;\n\t\tthis.setTextureCube = setTextureCube;\n\t\tthis.setTextureCubeDynamic = setTextureCubeDynamic;\n\t\tthis.setupRenderTarget = setupRenderTarget;\n\t\tthis.updateRenderTargetMipmap = updateRenderTargetMipmap;\n\n\t}\n\n\t/**\n\t * @author fordacious / fordacious.github.io\n\t */\n\n\tfunction WebGLProperties() {\n\n\t\tvar properties = {};\n\n\t\treturn {\n\n\t\t\tget: function ( object ) {\n\n\t\t\t\tvar uuid = object.uuid;\n\t\t\t\tvar map = properties[ uuid ];\n\n\t\t\t\tif ( map === undefined ) {\n\n\t\t\t\t\tmap = {};\n\t\t\t\t\tproperties[ uuid ] = map;\n\n\t\t\t\t}\n\n\t\t\t\treturn map;\n\n\t\t\t},\n\n\t\t\tdelete: function ( object ) {\n\n\t\t\t\tdelete properties[ object.uuid ];\n\n\t\t\t},\n\n\t\t\tclear: function () {\n\n\t\t\t\tproperties = {};\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLState( gl, extensions, paramThreeToGL ) {\n\n\t\tfunction ColorBuffer() {\n\n\t\t\tvar locked = false;\n\n\t\t\tvar color = new Vector4();\n\t\t\tvar currentColorMask = null;\n\t\t\tvar currentColorClear = new Vector4();\n\n\t\t\treturn {\n\n\t\t\t\tsetMask: function ( colorMask ) {\n\n\t\t\t\t\tif ( currentColorMask !== colorMask && ! locked ) {\n\n\t\t\t\t\t\tgl.colorMask( colorMask, colorMask, colorMask, colorMask );\n\t\t\t\t\t\tcurrentColorMask = colorMask;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\t\tlocked = lock;\n\n\t\t\t\t},\n\n\t\t\t\tsetClear: function ( r, g, b, a ) {\n\n\t\t\t\t\tcolor.set( r, g, b, a );\n\n\t\t\t\t\tif ( currentColorClear.equals( color ) === false ) {\n\n\t\t\t\t\t\tgl.clearColor( r, g, b, a );\n\t\t\t\t\t\tcurrentColorClear.copy( color );\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\treset: function () {\n\n\t\t\t\t\tlocked = false;\n\n\t\t\t\t\tcurrentColorMask = null;\n\t\t\t\t\tcurrentColorClear.set( 0, 0, 0, 1 );\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}\n\n\t\tfunction DepthBuffer() {\n\n\t\t\tvar locked = false;\n\n\t\t\tvar currentDepthMask = null;\n\t\t\tvar currentDepthFunc = null;\n\t\t\tvar currentDepthClear = null;\n\n\t\t\treturn {\n\n\t\t\t\tsetTest: function ( depthTest ) {\n\n\t\t\t\t\tif ( depthTest ) {\n\n\t\t\t\t\t\tenable( gl.DEPTH_TEST );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tdisable( gl.DEPTH_TEST );\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetMask: function ( depthMask ) {\n\n\t\t\t\t\tif ( currentDepthMask !== depthMask && ! locked ) {\n\n\t\t\t\t\t\tgl.depthMask( depthMask );\n\t\t\t\t\t\tcurrentDepthMask = depthMask;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetFunc: function ( depthFunc ) {\n\n\t\t\t\t\tif ( currentDepthFunc !== depthFunc ) {\n\n\t\t\t\t\t\tif ( depthFunc ) {\n\n\t\t\t\t\t\t\tswitch ( depthFunc ) {\n\n\t\t\t\t\t\t\t\tcase NeverDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.NEVER );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase AlwaysDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.ALWAYS );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase LessDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.LESS );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase LessEqualDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.LEQUAL );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase EqualDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.EQUAL );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase GreaterEqualDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.GEQUAL );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase GreaterDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.GREATER );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase NotEqualDepth:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.NOTEQUAL );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t\t\t\tgl.depthFunc( gl.LEQUAL );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.LEQUAL );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcurrentDepthFunc = depthFunc;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\t\tlocked = lock;\n\n\t\t\t\t},\n\n\t\t\t\tsetClear: function ( depth ) {\n\n\t\t\t\t\tif ( currentDepthClear !== depth ) {\n\n\t\t\t\t\t\tgl.clearDepth( depth );\n\t\t\t\t\t\tcurrentDepthClear = depth;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\treset: function () {\n\n\t\t\t\t\tlocked = false;\n\n\t\t\t\t\tcurrentDepthMask = null;\n\t\t\t\t\tcurrentDepthFunc = null;\n\t\t\t\t\tcurrentDepthClear = null;\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}\n\n\t\tfunction StencilBuffer() {\n\n\t\t\tvar locked = false;\n\n\t\t\tvar currentStencilMask = null;\n\t\t\tvar currentStencilFunc = null;\n\t\t\tvar currentStencilRef = null;\n\t\t\tvar currentStencilFuncMask = null;\n\t\t\tvar currentStencilFail = null;\n\t\t\tvar currentStencilZFail = null;\n\t\t\tvar currentStencilZPass = null;\n\t\t\tvar currentStencilClear = null;\n\n\t\t\treturn {\n\n\t\t\t\tsetTest: function ( stencilTest ) {\n\n\t\t\t\t\tif ( stencilTest ) {\n\n\t\t\t\t\t\tenable( gl.STENCIL_TEST );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tdisable( gl.STENCIL_TEST );\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetMask: function ( stencilMask ) {\n\n\t\t\t\t\tif ( currentStencilMask !== stencilMask && ! locked ) {\n\n\t\t\t\t\t\tgl.stencilMask( stencilMask );\n\t\t\t\t\t\tcurrentStencilMask = stencilMask;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetFunc: function ( stencilFunc, stencilRef, stencilMask ) {\n\n\t\t\t\t\tif ( currentStencilFunc !== stencilFunc ||\n\t\t\t\t\t currentStencilRef \t!== stencilRef \t||\n\t\t\t\t\t currentStencilFuncMask !== stencilMask ) {\n\n\t\t\t\t\t\tgl.stencilFunc( stencilFunc, stencilRef, stencilMask );\n\n\t\t\t\t\t\tcurrentStencilFunc = stencilFunc;\n\t\t\t\t\t\tcurrentStencilRef = stencilRef;\n\t\t\t\t\t\tcurrentStencilFuncMask = stencilMask;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetOp: function ( stencilFail, stencilZFail, stencilZPass ) {\n\n\t\t\t\t\tif ( currentStencilFail\t !== stencilFail \t||\n\t\t\t\t\t currentStencilZFail !== stencilZFail ||\n\t\t\t\t\t currentStencilZPass !== stencilZPass ) {\n\n\t\t\t\t\t\tgl.stencilOp( stencilFail, stencilZFail, stencilZPass );\n\n\t\t\t\t\t\tcurrentStencilFail = stencilFail;\n\t\t\t\t\t\tcurrentStencilZFail = stencilZFail;\n\t\t\t\t\t\tcurrentStencilZPass = stencilZPass;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\t\tlocked = lock;\n\n\t\t\t\t},\n\n\t\t\t\tsetClear: function ( stencil ) {\n\n\t\t\t\t\tif ( currentStencilClear !== stencil ) {\n\n\t\t\t\t\t\tgl.clearStencil( stencil );\n\t\t\t\t\t\tcurrentStencilClear = stencil;\n\n\t\t\t\t\t}\n\n\t\t\t\t},\n\n\t\t\t\treset: function () {\n\n\t\t\t\t\tlocked = false;\n\n\t\t\t\t\tcurrentStencilMask = null;\n\t\t\t\t\tcurrentStencilFunc = null;\n\t\t\t\t\tcurrentStencilRef = null;\n\t\t\t\t\tcurrentStencilFuncMask = null;\n\t\t\t\t\tcurrentStencilFail = null;\n\t\t\t\t\tcurrentStencilZFail = null;\n\t\t\t\t\tcurrentStencilZPass = null;\n\t\t\t\t\tcurrentStencilClear = null;\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}\n\n\t\t//\n\n\t\tvar colorBuffer = new ColorBuffer();\n\t\tvar depthBuffer = new DepthBuffer();\n\t\tvar stencilBuffer = new StencilBuffer();\n\n\t\tvar maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n\t\tvar newAttributes = new Uint8Array( maxVertexAttributes );\n\t\tvar enabledAttributes = new Uint8Array( maxVertexAttributes );\n\t\tvar attributeDivisors = new Uint8Array( maxVertexAttributes );\n\n\t\tvar capabilities = {};\n\n\t\tvar compressedTextureFormats = null;\n\n\t\tvar currentBlending = null;\n\t\tvar currentBlendEquation = null;\n\t\tvar currentBlendSrc = null;\n\t\tvar currentBlendDst = null;\n\t\tvar currentBlendEquationAlpha = null;\n\t\tvar currentBlendSrcAlpha = null;\n\t\tvar currentBlendDstAlpha = null;\n\t\tvar currentPremultipledAlpha = false;\n\n\t\tvar currentFlipSided = null;\n\t\tvar currentCullFace = null;\n\n\t\tvar currentLineWidth = null;\n\n\t\tvar currentPolygonOffsetFactor = null;\n\t\tvar currentPolygonOffsetUnits = null;\n\n\t\tvar currentScissorTest = null;\n\n\t\tvar maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\n\n\t\tvar currentTextureSlot = null;\n\t\tvar currentBoundTextures = {};\n\n\t\tvar currentScissor = new Vector4();\n\t\tvar currentViewport = new Vector4();\n\n\t\tfunction createTexture( type, target, count ) {\n\n\t\t\tvar data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.\n\t\t\tvar texture = gl.createTexture();\n\n\t\t\tgl.bindTexture( type, texture );\n\t\t\tgl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\n\t\t\tgl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\n\n\t\t\tfor ( var i = 0; i < count; i ++ ) {\n\n\t\t\t\tgl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );\n\n\t\t\t}\n\n\t\t\treturn texture;\n\n\t\t}\n\n\t\tvar emptyTextures = {};\n\t\temptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );\n\t\temptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );\n\n\t\t//\n\n\t\tfunction init() {\n\n\t\t\tclearColor( 0, 0, 0, 1 );\n\t\t\tclearDepth( 1 );\n\t\t\tclearStencil( 0 );\n\n\t\t\tenable( gl.DEPTH_TEST );\n\t\t\tsetDepthFunc( LessEqualDepth );\n\n\t\t\tsetFlipSided( false );\n\t\t\tsetCullFace( CullFaceBack );\n\t\t\tenable( gl.CULL_FACE );\n\n\t\t\tenable( gl.BLEND );\n\t\t\tsetBlending( NormalBlending );\n\n\t\t}\n\n\t\tfunction initAttributes() {\n\n\t\t\tfor ( var i = 0, l = newAttributes.length; i < l; i ++ ) {\n\n\t\t\t\tnewAttributes[ i ] = 0;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction enableAttribute( attribute ) {\n\n\t\t\tnewAttributes[ attribute ] = 1;\n\n\t\t\tif ( enabledAttributes[ attribute ] === 0 ) {\n\n\t\t\t\tgl.enableVertexAttribArray( attribute );\n\t\t\t\tenabledAttributes[ attribute ] = 1;\n\n\t\t\t}\n\n\t\t\tif ( attributeDivisors[ attribute ] !== 0 ) {\n\n\t\t\t\tvar extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n\t\t\t\textension.vertexAttribDivisorANGLE( attribute, 0 );\n\t\t\t\tattributeDivisors[ attribute ] = 0;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction enableAttributeAndDivisor( attribute, meshPerAttribute, extension ) {\n\n\t\t\tnewAttributes[ attribute ] = 1;\n\n\t\t\tif ( enabledAttributes[ attribute ] === 0 ) {\n\n\t\t\t\tgl.enableVertexAttribArray( attribute );\n\t\t\t\tenabledAttributes[ attribute ] = 1;\n\n\t\t\t}\n\n\t\t\tif ( attributeDivisors[ attribute ] !== meshPerAttribute ) {\n\n\t\t\t\textension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );\n\t\t\t\tattributeDivisors[ attribute ] = meshPerAttribute;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction disableUnusedAttributes() {\n\n\t\t\tfor ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) {\n\n\t\t\t\tif ( enabledAttributes[ i ] !== newAttributes[ i ] ) {\n\n\t\t\t\t\tgl.disableVertexAttribArray( i );\n\t\t\t\t\tenabledAttributes[ i ] = 0;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction enable( id ) {\n\n\t\t\tif ( capabilities[ id ] !== true ) {\n\n\t\t\t\tgl.enable( id );\n\t\t\t\tcapabilities[ id ] = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction disable( id ) {\n\n\t\t\tif ( capabilities[ id ] !== false ) {\n\n\t\t\t\tgl.disable( id );\n\t\t\t\tcapabilities[ id ] = false;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction getCompressedTextureFormats() {\n\n\t\t\tif ( compressedTextureFormats === null ) {\n\n\t\t\t\tcompressedTextureFormats = [];\n\n\t\t\t\tif ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||\n\t\t\t\t extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||\n\t\t\t\t extensions.get( 'WEBGL_compressed_texture_etc1' ) ) {\n\n\t\t\t\t\tvar formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );\n\n\t\t\t\t\tfor ( var i = 0; i < formats.length; i ++ ) {\n\n\t\t\t\t\t\tcompressedTextureFormats.push( formats[ i ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn compressedTextureFormats;\n\n\t\t}\n\n\t\tfunction setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {\n\n\t\t\tif ( blending !== NoBlending ) {\n\n\t\t\t\tenable( gl.BLEND );\n\n\t\t\t} else {\n\n\t\t\t\tdisable( gl.BLEND );\n\n\t\t\t}\n\n\t\t\tif ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {\n\n\t\t\t\tif ( blending === AdditiveBlending ) {\n\n\t\t\t\t\tif ( premultipliedAlpha ) {\n\n\t\t\t\t\t\tgl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tgl.blendEquation( gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( blending === SubtractiveBlending ) {\n\n\t\t\t\t\tif ( premultipliedAlpha ) {\n\n\t\t\t\t\t\tgl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tgl.blendEquation( gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( blending === MultiplyBlending ) {\n\n\t\t\t\t\tif ( premultipliedAlpha ) {\n\n\t\t\t\t\t\tgl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tgl.blendEquation( gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFunc( gl.ZERO, gl.SRC_COLOR );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( premultipliedAlpha ) {\n\n\t\t\t\t\t\tgl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tgl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n\t\t\t\t\t\tgl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tcurrentBlending = blending;\n\t\t\t\tcurrentPremultipledAlpha = premultipliedAlpha;\n\n\t\t\t}\n\n\t\t\tif ( blending === CustomBlending ) {\n\n\t\t\t\tblendEquationAlpha = blendEquationAlpha || blendEquation;\n\t\t\t\tblendSrcAlpha = blendSrcAlpha || blendSrc;\n\t\t\t\tblendDstAlpha = blendDstAlpha || blendDst;\n\n\t\t\t\tif ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {\n\n\t\t\t\t\tgl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) );\n\n\t\t\t\t\tcurrentBlendEquation = blendEquation;\n\t\t\t\t\tcurrentBlendEquationAlpha = blendEquationAlpha;\n\n\t\t\t\t}\n\n\t\t\t\tif ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {\n\n\t\t\t\t\tgl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) );\n\n\t\t\t\t\tcurrentBlendSrc = blendSrc;\n\t\t\t\t\tcurrentBlendDst = blendDst;\n\t\t\t\t\tcurrentBlendSrcAlpha = blendSrcAlpha;\n\t\t\t\t\tcurrentBlendDstAlpha = blendDstAlpha;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tcurrentBlendEquation = null;\n\t\t\t\tcurrentBlendSrc = null;\n\t\t\t\tcurrentBlendDst = null;\n\t\t\t\tcurrentBlendEquationAlpha = null;\n\t\t\t\tcurrentBlendSrcAlpha = null;\n\t\t\t\tcurrentBlendDstAlpha = null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// TODO Deprecate\n\n\t\tfunction setColorWrite( colorWrite ) {\n\n\t\t\tcolorBuffer.setMask( colorWrite );\n\n\t\t}\n\n\t\tfunction setDepthTest( depthTest ) {\n\n\t\t\tdepthBuffer.setTest( depthTest );\n\n\t\t}\n\n\t\tfunction setDepthWrite( depthWrite ) {\n\n\t\t\tdepthBuffer.setMask( depthWrite );\n\n\t\t}\n\n\t\tfunction setDepthFunc( depthFunc ) {\n\n\t\t\tdepthBuffer.setFunc( depthFunc );\n\n\t\t}\n\n\t\tfunction setStencilTest( stencilTest ) {\n\n\t\t\tstencilBuffer.setTest( stencilTest );\n\n\t\t}\n\n\t\tfunction setStencilWrite( stencilWrite ) {\n\n\t\t\tstencilBuffer.setMask( stencilWrite );\n\n\t\t}\n\n\t\tfunction setStencilFunc( stencilFunc, stencilRef, stencilMask ) {\n\n\t\t\tstencilBuffer.setFunc( stencilFunc, stencilRef, stencilMask );\n\n\t\t}\n\n\t\tfunction setStencilOp( stencilFail, stencilZFail, stencilZPass ) {\n\n\t\t\tstencilBuffer.setOp( stencilFail, stencilZFail, stencilZPass );\n\n\t\t}\n\n\t\t//\n\n\t\tfunction setFlipSided( flipSided ) {\n\n\t\t\tif ( currentFlipSided !== flipSided ) {\n\n\t\t\t\tif ( flipSided ) {\n\n\t\t\t\t\tgl.frontFace( gl.CW );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tgl.frontFace( gl.CCW );\n\n\t\t\t\t}\n\n\t\t\t\tcurrentFlipSided = flipSided;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction setCullFace( cullFace ) {\n\n\t\t\tif ( cullFace !== CullFaceNone ) {\n\n\t\t\t\tenable( gl.CULL_FACE );\n\n\t\t\t\tif ( cullFace !== currentCullFace ) {\n\n\t\t\t\t\tif ( cullFace === CullFaceBack ) {\n\n\t\t\t\t\t\tgl.cullFace( gl.BACK );\n\n\t\t\t\t\t} else if ( cullFace === CullFaceFront ) {\n\n\t\t\t\t\t\tgl.cullFace( gl.FRONT );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tgl.cullFace( gl.FRONT_AND_BACK );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tdisable( gl.CULL_FACE );\n\n\t\t\t}\n\n\t\t\tcurrentCullFace = cullFace;\n\n\t\t}\n\n\t\tfunction setLineWidth( width ) {\n\n\t\t\tif ( width !== currentLineWidth ) {\n\n\t\t\t\tgl.lineWidth( width );\n\n\t\t\t\tcurrentLineWidth = width;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction setPolygonOffset( polygonOffset, factor, units ) {\n\n\t\t\tif ( polygonOffset ) {\n\n\t\t\t\tenable( gl.POLYGON_OFFSET_FILL );\n\n\t\t\t\tif ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {\n\n\t\t\t\t\tgl.polygonOffset( factor, units );\n\n\t\t\t\t\tcurrentPolygonOffsetFactor = factor;\n\t\t\t\t\tcurrentPolygonOffsetUnits = units;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tdisable( gl.POLYGON_OFFSET_FILL );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction getScissorTest() {\n\n\t\t\treturn currentScissorTest;\n\n\t\t}\n\n\t\tfunction setScissorTest( scissorTest ) {\n\n\t\t\tcurrentScissorTest = scissorTest;\n\n\t\t\tif ( scissorTest ) {\n\n\t\t\t\tenable( gl.SCISSOR_TEST );\n\n\t\t\t} else {\n\n\t\t\t\tdisable( gl.SCISSOR_TEST );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// texture\n\n\t\tfunction activeTexture( webglSlot ) {\n\n\t\t\tif ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;\n\n\t\t\tif ( currentTextureSlot !== webglSlot ) {\n\n\t\t\t\tgl.activeTexture( webglSlot );\n\t\t\t\tcurrentTextureSlot = webglSlot;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction bindTexture( webglType, webglTexture ) {\n\n\t\t\tif ( currentTextureSlot === null ) {\n\n\t\t\t\tactiveTexture();\n\n\t\t\t}\n\n\t\t\tvar boundTexture = currentBoundTextures[ currentTextureSlot ];\n\n\t\t\tif ( boundTexture === undefined ) {\n\n\t\t\t\tboundTexture = { type: undefined, texture: undefined };\n\t\t\t\tcurrentBoundTextures[ currentTextureSlot ] = boundTexture;\n\n\t\t\t}\n\n\t\t\tif ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {\n\n\t\t\t\tgl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );\n\n\t\t\t\tboundTexture.type = webglType;\n\t\t\t\tboundTexture.texture = webglTexture;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction compressedTexImage2D() {\n\n\t\t\ttry {\n\n\t\t\t\tgl.compressedTexImage2D.apply( gl, arguments );\n\n\t\t\t} catch ( error ) {\n\n\t\t\t\tconsole.error( error );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction texImage2D() {\n\n\t\t\ttry {\n\n\t\t\t\tgl.texImage2D.apply( gl, arguments );\n\n\t\t\t} catch ( error ) {\n\n\t\t\t\tconsole.error( error );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// TODO Deprecate\n\n\t\tfunction clearColor( r, g, b, a ) {\n\n\t\t\tcolorBuffer.setClear( r, g, b, a );\n\n\t\t}\n\n\t\tfunction clearDepth( depth ) {\n\n\t\t\tdepthBuffer.setClear( depth );\n\n\t\t}\n\n\t\tfunction clearStencil( stencil ) {\n\n\t\t\tstencilBuffer.setClear( stencil );\n\n\t\t}\n\n\t\t//\n\n\t\tfunction scissor( scissor ) {\n\n\t\t\tif ( currentScissor.equals( scissor ) === false ) {\n\n\t\t\t\tgl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );\n\t\t\t\tcurrentScissor.copy( scissor );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction viewport( viewport ) {\n\n\t\t\tif ( currentViewport.equals( viewport ) === false ) {\n\n\t\t\t\tgl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );\n\t\t\t\tcurrentViewport.copy( viewport );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tfunction reset() {\n\n\t\t\tfor ( var i = 0; i < enabledAttributes.length; i ++ ) {\n\n\t\t\t\tif ( enabledAttributes[ i ] === 1 ) {\n\n\t\t\t\t\tgl.disableVertexAttribArray( i );\n\t\t\t\t\tenabledAttributes[ i ] = 0;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tcapabilities = {};\n\n\t\t\tcompressedTextureFormats = null;\n\n\t\t\tcurrentTextureSlot = null;\n\t\t\tcurrentBoundTextures = {};\n\n\t\t\tcurrentBlending = null;\n\n\t\t\tcurrentFlipSided = null;\n\t\t\tcurrentCullFace = null;\n\n\t\t\tcolorBuffer.reset();\n\t\t\tdepthBuffer.reset();\n\t\t\tstencilBuffer.reset();\n\n\t\t}\n\n\t\treturn {\n\n\t\t\tbuffers: {\n\t\t\t\tcolor: colorBuffer,\n\t\t\t\tdepth: depthBuffer,\n\t\t\t\tstencil: stencilBuffer\n\t\t\t},\n\n\t\t\tinit: init,\n\t\t\tinitAttributes: initAttributes,\n\t\t\tenableAttribute: enableAttribute,\n\t\t\tenableAttributeAndDivisor: enableAttributeAndDivisor,\n\t\t\tdisableUnusedAttributes: disableUnusedAttributes,\n\t\t\tenable: enable,\n\t\t\tdisable: disable,\n\t\t\tgetCompressedTextureFormats: getCompressedTextureFormats,\n\n\t\t\tsetBlending: setBlending,\n\n\t\t\tsetColorWrite: setColorWrite,\n\t\t\tsetDepthTest: setDepthTest,\n\t\t\tsetDepthWrite: setDepthWrite,\n\t\t\tsetDepthFunc: setDepthFunc,\n\t\t\tsetStencilTest: setStencilTest,\n\t\t\tsetStencilWrite: setStencilWrite,\n\t\t\tsetStencilFunc: setStencilFunc,\n\t\t\tsetStencilOp: setStencilOp,\n\n\t\t\tsetFlipSided: setFlipSided,\n\t\t\tsetCullFace: setCullFace,\n\n\t\t\tsetLineWidth: setLineWidth,\n\t\t\tsetPolygonOffset: setPolygonOffset,\n\n\t\t\tgetScissorTest: getScissorTest,\n\t\t\tsetScissorTest: setScissorTest,\n\n\t\t\tactiveTexture: activeTexture,\n\t\t\tbindTexture: bindTexture,\n\t\t\tcompressedTexImage2D: compressedTexImage2D,\n\t\t\ttexImage2D: texImage2D,\n\n\t\t\tclearColor: clearColor,\n\t\t\tclearDepth: clearDepth,\n\t\t\tclearStencil: clearStencil,\n\n\t\t\tscissor: scissor,\n\t\t\tviewport: viewport,\n\n\t\t\treset: reset\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLCapabilities( gl, extensions, parameters ) {\n\n\t\tvar maxAnisotropy;\n\n\t\tfunction getMaxAnisotropy() {\n\n\t\t\tif ( maxAnisotropy !== undefined ) return maxAnisotropy;\n\n\t\t\tvar extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tmaxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );\n\n\t\t\t} else {\n\n\t\t\t\tmaxAnisotropy = 0;\n\n\t\t\t}\n\n\t\t\treturn maxAnisotropy;\n\n\t\t}\n\n\t\tfunction getMaxPrecision( precision ) {\n\n\t\t\tif ( precision === 'highp' ) {\n\n\t\t\t\tif ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&\n\t\t\t\t gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {\n\n\t\t\t\t\treturn 'highp';\n\n\t\t\t\t}\n\n\t\t\t\tprecision = 'mediump';\n\n\t\t\t}\n\n\t\t\tif ( precision === 'mediump' ) {\n\n\t\t\t\tif ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&\n\t\t\t\t gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {\n\n\t\t\t\t\treturn 'mediump';\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn 'lowp';\n\n\t\t}\n\n\t\tvar precision = parameters.precision !== undefined ? parameters.precision : 'highp';\n\t\tvar maxPrecision = getMaxPrecision( precision );\n\n\t\tif ( maxPrecision !== precision ) {\n\n\t\t\tconsole.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );\n\t\t\tprecision = maxPrecision;\n\n\t\t}\n\n\t\tvar logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true && !! extensions.get( 'EXT_frag_depth' );\n\n\t\tvar maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\n\t\tvar maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );\n\t\tvar maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );\n\t\tvar maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );\n\n\t\tvar maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n\t\tvar maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );\n\t\tvar maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );\n\t\tvar maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );\n\n\t\tvar vertexTextures = maxVertexTextures > 0;\n\t\tvar floatFragmentTextures = !! extensions.get( 'OES_texture_float' );\n\t\tvar floatVertexTextures = vertexTextures && floatFragmentTextures;\n\n\t\treturn {\n\n\t\t\tgetMaxAnisotropy: getMaxAnisotropy,\n\t\t\tgetMaxPrecision: getMaxPrecision,\n\n\t\t\tprecision: precision,\n\t\t\tlogarithmicDepthBuffer: logarithmicDepthBuffer,\n\n\t\t\tmaxTextures: maxTextures,\n\t\t\tmaxVertexTextures: maxVertexTextures,\n\t\t\tmaxTextureSize: maxTextureSize,\n\t\t\tmaxCubemapSize: maxCubemapSize,\n\n\t\t\tmaxAttributes: maxAttributes,\n\t\t\tmaxVertexUniforms: maxVertexUniforms,\n\t\t\tmaxVaryings: maxVaryings,\n\t\t\tmaxFragmentUniforms: maxFragmentUniforms,\n\n\t\t\tvertexTextures: vertexTextures,\n\t\t\tfloatFragmentTextures: floatFragmentTextures,\n\t\t\tfloatVertexTextures: floatVertexTextures\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WebGLExtensions( gl ) {\n\n\t\tvar extensions = {};\n\n\t\treturn {\n\n\t\t\tget: function ( name ) {\n\n\t\t\t\tif ( extensions[ name ] !== undefined ) {\n\n\t\t\t\t\treturn extensions[ name ];\n\n\t\t\t\t}\n\n\t\t\t\tvar extension;\n\n\t\t\t\tswitch ( name ) {\n\n\t\t\t\t\tcase 'WEBGL_depth_texture':\n\t\t\t\t\t\textension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'EXT_texture_filter_anisotropic':\n\t\t\t\t\t\textension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'WEBGL_compressed_texture_s3tc':\n\t\t\t\t\t\textension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'WEBGL_compressed_texture_pvrtc':\n\t\t\t\t\t\textension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'WEBGL_compressed_texture_etc1':\n\t\t\t\t\t\textension = gl.getExtension( 'WEBGL_compressed_texture_etc1' );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\textension = gl.getExtension( name );\n\n\t\t\t\t}\n\n\t\t\t\tif ( extension === null ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );\n\n\t\t\t\t}\n\n\t\t\t\textensions[ name ] = extension;\n\n\t\t\t\treturn extension;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\t/**\n\t * @author tschw\n\t */\n\n\tfunction WebGLClipping() {\n\n\t\tvar scope = this,\n\n\t\t\tglobalState = null,\n\t\t\tnumGlobalPlanes = 0,\n\t\t\tlocalClippingEnabled = false,\n\t\t\trenderingShadows = false,\n\n\t\t\tplane = new Plane(),\n\t\t\tviewNormalMatrix = new Matrix3(),\n\n\t\t\tuniform = { value: null, needsUpdate: false };\n\n\t\tthis.uniform = uniform;\n\t\tthis.numPlanes = 0;\n\t\tthis.numIntersection = 0;\n\n\t\tthis.init = function( planes, enableLocalClipping, camera ) {\n\n\t\t\tvar enabled =\n\t\t\t\tplanes.length !== 0 ||\n\t\t\t\tenableLocalClipping ||\n\t\t\t\t// enable state of previous frame - the clipping code has to\n\t\t\t\t// run another frame in order to reset the state:\n\t\t\t\tnumGlobalPlanes !== 0 ||\n\t\t\t\tlocalClippingEnabled;\n\n\t\t\tlocalClippingEnabled = enableLocalClipping;\n\n\t\t\tglobalState = projectPlanes( planes, camera, 0 );\n\t\t\tnumGlobalPlanes = planes.length;\n\n\t\t\treturn enabled;\n\n\t\t};\n\n\t\tthis.beginShadows = function() {\n\n\t\t\trenderingShadows = true;\n\t\t\tprojectPlanes( null );\n\n\t\t};\n\n\t\tthis.endShadows = function() {\n\n\t\t\trenderingShadows = false;\n\t\t\tresetGlobalState();\n\n\t\t};\n\n\t\tthis.setState = function( planes, clipIntersection, clipShadows, camera, cache, fromCache ) {\n\n\t\t\tif ( ! localClippingEnabled ||\n\t\t\t\t\tplanes === null || planes.length === 0 ||\n\t\t\t\t\trenderingShadows && ! clipShadows ) {\n\t\t\t\t// there's no local clipping\n\n\t\t\t\tif ( renderingShadows ) {\n\t\t\t\t\t// there's no global clipping\n\n\t\t\t\t\tprojectPlanes( null );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tresetGlobalState();\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tvar nGlobal = renderingShadows ? 0 : numGlobalPlanes,\n\t\t\t\t\tlGlobal = nGlobal * 4,\n\n\t\t\t\t\tdstArray = cache.clippingState || null;\n\n\t\t\t\tuniform.value = dstArray; // ensure unique state\n\n\t\t\t\tdstArray = projectPlanes( planes, camera, lGlobal, fromCache );\n\n\t\t\t\tfor ( var i = 0; i !== lGlobal; ++ i ) {\n\n\t\t\t\t\tdstArray[ i ] = globalState[ i ];\n\n\t\t\t\t}\n\n\t\t\t\tcache.clippingState = dstArray;\n\t\t\t\tthis.numIntersection = clipIntersection ? this.numPlanes : 0;\n\t\t\t\tthis.numPlanes += nGlobal;\n\n\t\t\t}\n\n\n\t\t};\n\n\t\tfunction resetGlobalState() {\n\n\t\t\tif ( uniform.value !== globalState ) {\n\n\t\t\t\tuniform.value = globalState;\n\t\t\t\tuniform.needsUpdate = numGlobalPlanes > 0;\n\n\t\t\t}\n\n\t\t\tscope.numPlanes = numGlobalPlanes;\n\t\t\tscope.numIntersection = 0;\n\n\t\t}\n\n\t\tfunction projectPlanes( planes, camera, dstOffset, skipTransform ) {\n\n\t\t\tvar nPlanes = planes !== null ? planes.length : 0,\n\t\t\t\tdstArray = null;\n\n\t\t\tif ( nPlanes !== 0 ) {\n\n\t\t\t\tdstArray = uniform.value;\n\n\t\t\t\tif ( skipTransform !== true || dstArray === null ) {\n\n\t\t\t\t\tvar flatSize = dstOffset + nPlanes * 4,\n\t\t\t\t\t\tviewMatrix = camera.matrixWorldInverse;\n\n\t\t\t\t\tviewNormalMatrix.getNormalMatrix( viewMatrix );\n\n\t\t\t\t\tif ( dstArray === null || dstArray.length < flatSize ) {\n\n\t\t\t\t\t\tdstArray = new Float32Array( flatSize );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( var i = 0, i4 = dstOffset;\n\t\t\t\t\t\t\t\t\t\ti !== nPlanes; ++ i, i4 += 4 ) {\n\n\t\t\t\t\t\tplane.copy( planes[ i ] ).\n\t\t\t\t\t\t\t\tapplyMatrix4( viewMatrix, viewNormalMatrix );\n\n\t\t\t\t\t\tplane.normal.toArray( dstArray, i4 );\n\t\t\t\t\t\tdstArray[ i4 + 3 ] = plane.constant;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tuniform.value = dstArray;\n\t\t\t\tuniform.needsUpdate = true;\n\n\t\t\t}\n\n\t\t\tscope.numPlanes = nPlanes;\n\t\t\t\n\t\t\treturn dstArray;\n\n\t\t}\n\n\t}\n\n\t/**\n\t * @author supereggbert / http://www.paulbrunt.co.uk/\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author szimek / https://github.com/szimek/\n\t * @author tschw\n\t */\n\n\tfunction WebGLRenderer( parameters ) {\n\n\t\tconsole.log( 'THREE.WebGLRenderer', REVISION );\n\n\t\tparameters = parameters || {};\n\n\t\tvar _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),\n\t\t_context = parameters.context !== undefined ? parameters.context : null,\n\n\t\t_alpha = parameters.alpha !== undefined ? parameters.alpha : false,\n\t\t_depth = parameters.depth !== undefined ? parameters.depth : true,\n\t\t_stencil = parameters.stencil !== undefined ? parameters.stencil : true,\n\t\t_antialias = parameters.antialias !== undefined ? parameters.antialias : false,\n\t\t_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,\n\t\t_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false;\n\n\t\tvar lights = [];\n\n\t\tvar opaqueObjects = [];\n\t\tvar opaqueObjectsLastIndex = - 1;\n\t\tvar transparentObjects = [];\n\t\tvar transparentObjectsLastIndex = - 1;\n\n\t\tvar morphInfluences = new Float32Array( 8 );\n\n\t\tvar sprites = [];\n\t\tvar lensFlares = [];\n\n\t\t// public properties\n\n\t\tthis.domElement = _canvas;\n\t\tthis.context = null;\n\n\t\t// clearing\n\n\t\tthis.autoClear = true;\n\t\tthis.autoClearColor = true;\n\t\tthis.autoClearDepth = true;\n\t\tthis.autoClearStencil = true;\n\n\t\t// scene graph\n\n\t\tthis.sortObjects = true;\n\n\t\t// user-defined clipping\n\n\t\tthis.clippingPlanes = [];\n\t\tthis.localClippingEnabled = false;\n\n\t\t// physically based shading\n\n\t\tthis.gammaFactor = 2.0;\t// for backwards compatibility\n\t\tthis.gammaInput = false;\n\t\tthis.gammaOutput = false;\n\n\t\t// physical lights\n\n\t\tthis.physicallyCorrectLights = false;\n\n\t\t// tone mapping\n\n\t\tthis.toneMapping = LinearToneMapping;\n\t\tthis.toneMappingExposure = 1.0;\n\t\tthis.toneMappingWhitePoint = 1.0;\n\n\t\t// morphs\n\n\t\tthis.maxMorphTargets = 8;\n\t\tthis.maxMorphNormals = 4;\n\n\t\t// internal properties\n\n\t\tvar _this = this,\n\n\t\t// internal state cache\n\n\t\t_currentProgram = null,\n\t\t_currentRenderTarget = null,\n\t\t_currentFramebuffer = null,\n\t\t_currentMaterialId = - 1,\n\t\t_currentGeometryProgram = '',\n\t\t_currentCamera = null,\n\n\t\t_currentScissor = new Vector4(),\n\t\t_currentScissorTest = null,\n\n\t\t_currentViewport = new Vector4(),\n\n\t\t//\n\n\t\t_usedTextureUnits = 0,\n\n\t\t//\n\n\t\t_clearColor = new Color( 0x000000 ),\n\t\t_clearAlpha = 0,\n\n\t\t_width = _canvas.width,\n\t\t_height = _canvas.height,\n\n\t\t_pixelRatio = 1,\n\n\t\t_scissor = new Vector4( 0, 0, _width, _height ),\n\t\t_scissorTest = false,\n\n\t\t_viewport = new Vector4( 0, 0, _width, _height ),\n\n\t\t// frustum\n\n\t\t_frustum = new Frustum(),\n\n\t\t// clipping\n\n\t\t_clipping = new WebGLClipping(),\n\t\t_clippingEnabled = false,\n\t\t_localClippingEnabled = false,\n\n\t\t_sphere = new Sphere(),\n\n\t\t// camera matrices cache\n\n\t\t_projScreenMatrix = new Matrix4(),\n\n\t\t_vector3 = new Vector3(),\n\n\t\t// light arrays cache\n\n\t\t_lights = {\n\n\t\t\thash: '',\n\n\t\t\tambient: [ 0, 0, 0 ],\n\t\t\tdirectional: [],\n\t\t\tdirectionalShadowMap: [],\n\t\t\tdirectionalShadowMatrix: [],\n\t\t\tspot: [],\n\t\t\tspotShadowMap: [],\n\t\t\tspotShadowMatrix: [],\n\t\t\tpoint: [],\n\t\t\tpointShadowMap: [],\n\t\t\tpointShadowMatrix: [],\n\t\t\themi: [],\n\n\t\t\tshadows: []\n\n\t\t},\n\n\t\t// info\n\n\t\t_infoRender = {\n\n\t\t\tcalls: 0,\n\t\t\tvertices: 0,\n\t\t\tfaces: 0,\n\t\t\tpoints: 0\n\n\t\t};\n\n\t\tthis.info = {\n\n\t\t\trender: _infoRender,\n\t\t\tmemory: {\n\n\t\t\t\tgeometries: 0,\n\t\t\t\ttextures: 0\n\n\t\t\t},\n\t\t\tprograms: null\n\n\t\t};\n\n\n\t\t// initialize\n\n\t\tvar _gl;\n\n\t\ttry {\n\n\t\t\tvar attributes = {\n\t\t\t\talpha: _alpha,\n\t\t\t\tdepth: _depth,\n\t\t\t\tstencil: _stencil,\n\t\t\t\tantialias: _antialias,\n\t\t\t\tpremultipliedAlpha: _premultipliedAlpha,\n\t\t\t\tpreserveDrawingBuffer: _preserveDrawingBuffer\n\t\t\t};\n\n\t\t\t_gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );\n\n\t\t\tif ( _gl === null ) {\n\n\t\t\t\tif ( _canvas.getContext( 'webgl' ) !== null ) {\n\n\t\t\t\t\tthrow 'Error creating WebGL context with your selected attributes.';\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthrow 'Error creating WebGL context.';\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Some experimental-webgl implementations do not have getShaderPrecisionFormat\n\n\t\t\tif ( _gl.getShaderPrecisionFormat === undefined ) {\n\n\t\t\t\t_gl.getShaderPrecisionFormat = function () {\n\n\t\t\t\t\treturn { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };\n\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\t_canvas.addEventListener( 'webglcontextlost', onContextLost, false );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLRenderer: ' + error );\n\n\t\t}\n\n\t\tvar extensions = new WebGLExtensions( _gl );\n\n\t\textensions.get( 'WEBGL_depth_texture' );\n\t\textensions.get( 'OES_texture_float' );\n\t\textensions.get( 'OES_texture_float_linear' );\n\t\textensions.get( 'OES_texture_half_float' );\n\t\textensions.get( 'OES_texture_half_float_linear' );\n\t\textensions.get( 'OES_standard_derivatives' );\n\t\textensions.get( 'ANGLE_instanced_arrays' );\n\n\t\tif ( extensions.get( 'OES_element_index_uint' ) ) {\n\n\t\t\tBufferGeometry.MaxIndex = 4294967296;\n\n\t\t}\n\n\t\tvar capabilities = new WebGLCapabilities( _gl, extensions, parameters );\n\n\t\tvar state = new WebGLState( _gl, extensions, paramThreeToGL );\n\t\tvar properties = new WebGLProperties();\n\t\tvar textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, paramThreeToGL, this.info );\n\t\tvar objects = new WebGLObjects( _gl, properties, this.info );\n\t\tvar programCache = new WebGLPrograms( this, capabilities );\n\t\tvar lightCache = new WebGLLights();\n\n\t\tthis.info.programs = programCache.programs;\n\n\t\tvar bufferRenderer = new WebGLBufferRenderer( _gl, extensions, _infoRender );\n\t\tvar indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, _infoRender );\n\n\t\t//\n\n\t\tvar backgroundCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );\n\t\tvar backgroundCamera2 = new PerspectiveCamera();\n\t\tvar backgroundPlaneMesh = new Mesh(\n\t\t\tnew PlaneBufferGeometry( 2, 2 ),\n\t\t\tnew MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } )\n\t\t);\n\t\tvar backgroundBoxShader = ShaderLib[ 'cube' ];\n\t\tvar backgroundBoxMesh = new Mesh(\n\t\t\tnew BoxBufferGeometry( 5, 5, 5 ),\n\t\t\tnew ShaderMaterial( {\n\t\t\t\tuniforms: backgroundBoxShader.uniforms,\n\t\t\t\tvertexShader: backgroundBoxShader.vertexShader,\n\t\t\t\tfragmentShader: backgroundBoxShader.fragmentShader,\n\t\t\t\tside: BackSide,\n\t\t\t\tdepthTest: false,\n\t\t\t\tdepthWrite: false,\n\t\t\t\tfog: false\n\t\t\t} )\n\t\t);\n\n\t\t//\n\n\t\tfunction getTargetPixelRatio() {\n\n\t\t\treturn _currentRenderTarget === null ? _pixelRatio : 1;\n\n\t\t}\n\n\t\tfunction glClearColor( r, g, b, a ) {\n\n\t\t\tif ( _premultipliedAlpha === true ) {\n\n\t\t\t\tr *= a; g *= a; b *= a;\n\n\t\t\t}\n\n\t\t\tstate.clearColor( r, g, b, a );\n\n\t\t}\n\n\t\tfunction setDefaultGLState() {\n\n\t\t\tstate.init();\n\n\t\t\tstate.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );\n\t\t\tstate.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );\n\n\t\t\tglClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );\n\n\t\t}\n\n\t\tfunction resetGLState() {\n\n\t\t\t_currentProgram = null;\n\t\t\t_currentCamera = null;\n\n\t\t\t_currentGeometryProgram = '';\n\t\t\t_currentMaterialId = - 1;\n\n\t\t\tstate.reset();\n\n\t\t}\n\n\t\tsetDefaultGLState();\n\n\t\tthis.context = _gl;\n\t\tthis.capabilities = capabilities;\n\t\tthis.extensions = extensions;\n\t\tthis.properties = properties;\n\t\tthis.state = state;\n\n\t\t// shadow map\n\n\t\tvar shadowMap = new WebGLShadowMap( this, _lights, objects, capabilities );\n\n\t\tthis.shadowMap = shadowMap;\n\n\n\t\t// Plugins\n\n\t\tvar spritePlugin = new SpritePlugin( this, sprites );\n\t\tvar lensFlarePlugin = new LensFlarePlugin( this, lensFlares );\n\n\t\t// API\n\n\t\tthis.getContext = function () {\n\n\t\t\treturn _gl;\n\n\t\t};\n\n\t\tthis.getContextAttributes = function () {\n\n\t\t\treturn _gl.getContextAttributes();\n\n\t\t};\n\n\t\tthis.forceContextLoss = function () {\n\n\t\t\textensions.get( 'WEBGL_lose_context' ).loseContext();\n\n\t\t};\n\n\t\tthis.getMaxAnisotropy = function () {\n\n\t\t\treturn capabilities.getMaxAnisotropy();\n\n\t\t};\n\n\t\tthis.getPrecision = function () {\n\n\t\t\treturn capabilities.precision;\n\n\t\t};\n\n\t\tthis.getPixelRatio = function () {\n\n\t\t\treturn _pixelRatio;\n\n\t\t};\n\n\t\tthis.setPixelRatio = function ( value ) {\n\n\t\t\tif ( value === undefined ) return;\n\n\t\t\t_pixelRatio = value;\n\n\t\t\tthis.setSize( _viewport.z, _viewport.w, false );\n\n\t\t};\n\n\t\tthis.getSize = function () {\n\n\t\t\treturn {\n\t\t\t\twidth: _width,\n\t\t\t\theight: _height\n\t\t\t};\n\n\t\t};\n\n\t\tthis.setSize = function ( width, height, updateStyle ) {\n\n\t\t\t_width = width;\n\t\t\t_height = height;\n\n\t\t\t_canvas.width = width * _pixelRatio;\n\t\t\t_canvas.height = height * _pixelRatio;\n\n\t\t\tif ( updateStyle !== false ) {\n\n\t\t\t\t_canvas.style.width = width + 'px';\n\t\t\t\t_canvas.style.height = height + 'px';\n\n\t\t\t}\n\n\t\t\tthis.setViewport( 0, 0, width, height );\n\n\t\t};\n\n\t\tthis.setViewport = function ( x, y, width, height ) {\n\n\t\t\tstate.viewport( _viewport.set( x, y, width, height ) );\n\n\t\t};\n\n\t\tthis.setScissor = function ( x, y, width, height ) {\n\n\t\t\tstate.scissor( _scissor.set( x, y, width, height ) );\n\n\t\t};\n\n\t\tthis.setScissorTest = function ( boolean ) {\n\n\t\t\tstate.setScissorTest( _scissorTest = boolean );\n\n\t\t};\n\n\t\t// Clearing\n\n\t\tthis.getClearColor = function () {\n\n\t\t\treturn _clearColor;\n\n\t\t};\n\n\t\tthis.setClearColor = function ( color, alpha ) {\n\n\t\t\t_clearColor.set( color );\n\n\t\t\t_clearAlpha = alpha !== undefined ? alpha : 1;\n\n\t\t\tglClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );\n\n\t\t};\n\n\t\tthis.getClearAlpha = function () {\n\n\t\t\treturn _clearAlpha;\n\n\t\t};\n\n\t\tthis.setClearAlpha = function ( alpha ) {\n\n\t\t\t_clearAlpha = alpha;\n\n\t\t\tglClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );\n\n\t\t};\n\n\t\tthis.clear = function ( color, depth, stencil ) {\n\n\t\t\tvar bits = 0;\n\n\t\t\tif ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;\n\t\t\tif ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;\n\t\t\tif ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;\n\n\t\t\t_gl.clear( bits );\n\n\t\t};\n\n\t\tthis.clearColor = function () {\n\n\t\t\tthis.clear( true, false, false );\n\n\t\t};\n\n\t\tthis.clearDepth = function () {\n\n\t\t\tthis.clear( false, true, false );\n\n\t\t};\n\n\t\tthis.clearStencil = function () {\n\n\t\t\tthis.clear( false, false, true );\n\n\t\t};\n\n\t\tthis.clearTarget = function ( renderTarget, color, depth, stencil ) {\n\n\t\t\tthis.setRenderTarget( renderTarget );\n\t\t\tthis.clear( color, depth, stencil );\n\n\t\t};\n\n\t\t// Reset\n\n\t\tthis.resetGLState = resetGLState;\n\n\t\tthis.dispose = function() {\n\n\t\t\ttransparentObjects = [];\n\t\t\ttransparentObjectsLastIndex = -1;\n\t\t\topaqueObjects = [];\n\t\t\topaqueObjectsLastIndex = -1;\n\n\t\t\t_canvas.removeEventListener( 'webglcontextlost', onContextLost, false );\n\n\t\t};\n\n\t\t// Events\n\n\t\tfunction onContextLost( event ) {\n\n\t\t\tevent.preventDefault();\n\n\t\t\tresetGLState();\n\t\t\tsetDefaultGLState();\n\n\t\t\tproperties.clear();\n\n\t\t}\n\n\t\tfunction onMaterialDispose( event ) {\n\n\t\t\tvar material = event.target;\n\n\t\t\tmaterial.removeEventListener( 'dispose', onMaterialDispose );\n\n\t\t\tdeallocateMaterial( material );\n\n\t\t}\n\n\t\t// Buffer deallocation\n\n\t\tfunction deallocateMaterial( material ) {\n\n\t\t\treleaseMaterialProgramReference( material );\n\n\t\t\tproperties.delete( material );\n\n\t\t}\n\n\n\t\tfunction releaseMaterialProgramReference( material ) {\n\n\t\t\tvar programInfo = properties.get( material ).program;\n\n\t\t\tmaterial.program = undefined;\n\n\t\t\tif ( programInfo !== undefined ) {\n\n\t\t\t\tprogramCache.releaseProgram( programInfo );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Buffer rendering\n\n\t\tthis.renderBufferImmediate = function ( object, program, material ) {\n\n\t\t\tstate.initAttributes();\n\n\t\t\tvar buffers = properties.get( object );\n\n\t\t\tif ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();\n\t\t\tif ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();\n\t\t\tif ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();\n\t\t\tif ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();\n\n\t\t\tvar attributes = program.getAttributes();\n\n\t\t\tif ( object.hasPositions ) {\n\n\t\t\t\t_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );\n\t\t\t\t_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );\n\n\t\t\t\tstate.enableAttribute( attributes.position );\n\t\t\t\t_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );\n\n\t\t\t}\n\n\t\t\tif ( object.hasNormals ) {\n\n\t\t\t\t_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );\n\n\t\t\t\tif ( ! material.isMeshPhongMaterial &&\n\t\t\t\t ! material.isMeshStandardMaterial &&\n\t\t\t\t material.shading === FlatShading ) {\n\n\t\t\t\t\tfor ( var i = 0, l = object.count * 3; i < l; i += 9 ) {\n\n\t\t\t\t\t\tvar array = object.normalArray;\n\n\t\t\t\t\t\tvar nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3;\n\t\t\t\t\t\tvar ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3;\n\t\t\t\t\t\tvar nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3;\n\n\t\t\t\t\t\tarray[ i + 0 ] = nx;\n\t\t\t\t\t\tarray[ i + 1 ] = ny;\n\t\t\t\t\t\tarray[ i + 2 ] = nz;\n\n\t\t\t\t\t\tarray[ i + 3 ] = nx;\n\t\t\t\t\t\tarray[ i + 4 ] = ny;\n\t\t\t\t\t\tarray[ i + 5 ] = nz;\n\n\t\t\t\t\t\tarray[ i + 6 ] = nx;\n\t\t\t\t\t\tarray[ i + 7 ] = ny;\n\t\t\t\t\t\tarray[ i + 8 ] = nz;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );\n\n\t\t\t\tstate.enableAttribute( attributes.normal );\n\n\t\t\t\t_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );\n\n\t\t\t}\n\n\t\t\tif ( object.hasUvs && material.map ) {\n\n\t\t\t\t_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );\n\t\t\t\t_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );\n\n\t\t\t\tstate.enableAttribute( attributes.uv );\n\n\t\t\t\t_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );\n\n\t\t\t}\n\n\t\t\tif ( object.hasColors && material.vertexColors !== NoColors ) {\n\n\t\t\t\t_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );\n\t\t\t\t_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );\n\n\t\t\t\tstate.enableAttribute( attributes.color );\n\n\t\t\t\t_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );\n\n\t\t\t}\n\n\t\t\tstate.disableUnusedAttributes();\n\n\t\t\t_gl.drawArrays( _gl.TRIANGLES, 0, object.count );\n\n\t\t\tobject.count = 0;\n\n\t\t};\n\n\t\tthis.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) {\n\n\t\t\tsetMaterial( material );\n\n\t\t\tvar program = setProgram( camera, fog, material, object );\n\n\t\t\tvar updateBuffers = false;\n\t\t\tvar geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe;\n\n\t\t\tif ( geometryProgram !== _currentGeometryProgram ) {\n\n\t\t\t\t_currentGeometryProgram = geometryProgram;\n\t\t\t\tupdateBuffers = true;\n\n\t\t\t}\n\n\t\t\t// morph targets\n\n\t\t\tvar morphTargetInfluences = object.morphTargetInfluences;\n\n\t\t\tif ( morphTargetInfluences !== undefined ) {\n\n\t\t\t\tvar activeInfluences = [];\n\n\t\t\t\tfor ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) {\n\n\t\t\t\t\tvar influence = morphTargetInfluences[ i ];\n\t\t\t\t\tactiveInfluences.push( [ influence, i ] );\n\n\t\t\t\t}\n\n\t\t\t\tactiveInfluences.sort( absNumericalSort );\n\n\t\t\t\tif ( activeInfluences.length > 8 ) {\n\n\t\t\t\t\tactiveInfluences.length = 8;\n\n\t\t\t\t}\n\n\t\t\t\tvar morphAttributes = geometry.morphAttributes;\n\n\t\t\t\tfor ( var i = 0, l = activeInfluences.length; i < l; i ++ ) {\n\n\t\t\t\t\tvar influence = activeInfluences[ i ];\n\t\t\t\t\tmorphInfluences[ i ] = influence[ 0 ];\n\n\t\t\t\t\tif ( influence[ 0 ] !== 0 ) {\n\n\t\t\t\t\t\tvar index = influence[ 1 ];\n\n\t\t\t\t\t\tif ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] );\n\t\t\t\t\t\tif ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i );\n\t\t\t\t\t\tif ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tfor ( var i = activeInfluences.length, il = morphInfluences.length; i < il; i ++ ) {\n\n\t\t\t\t\tmorphInfluences[ i ] = 0.0;\n\n\t\t\t\t}\n\n\t\t\t\tprogram.getUniforms().setValue(\n\t\t\t\t\t\t_gl, 'morphTargetInfluences', morphInfluences );\n\n\t\t\t\tupdateBuffers = true;\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tvar index = geometry.index;\n\t\t\tvar position = geometry.attributes.position;\n\t\t\tvar rangeFactor = 1;\n\n\t\t\tif ( material.wireframe === true ) {\n\n\t\t\t\tindex = objects.getWireframeAttribute( geometry );\n\t\t\t\trangeFactor = 2;\n\n\t\t\t}\n\n\t\t\tvar renderer;\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\trenderer = indexedBufferRenderer;\n\t\t\t\trenderer.setIndex( index );\n\n\t\t\t} else {\n\n\t\t\t\trenderer = bufferRenderer;\n\n\t\t\t}\n\n\t\t\tif ( updateBuffers ) {\n\n\t\t\t\tsetupVertexAttributes( material, program, geometry );\n\n\t\t\t\tif ( index !== null ) {\n\n\t\t\t\t\t_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tvar dataCount = 0;\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tdataCount = index.count;\n\n\t\t\t} else if ( position !== undefined ) {\n\n\t\t\t\tdataCount = position.count;\n\n\t\t\t}\n\n\t\t\tvar rangeStart = geometry.drawRange.start * rangeFactor;\n\t\t\tvar rangeCount = geometry.drawRange.count * rangeFactor;\n\n\t\t\tvar groupStart = group !== null ? group.start * rangeFactor : 0;\n\t\t\tvar groupCount = group !== null ? group.count * rangeFactor : Infinity;\n\n\t\t\tvar drawStart = Math.max( rangeStart, groupStart );\n\t\t\tvar drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;\n\n\t\t\tvar drawCount = Math.max( 0, drawEnd - drawStart + 1 );\n\n\t\t\tif ( drawCount === 0 ) return;\n\n\t\t\t//\n\n\t\t\tif ( object.isMesh ) {\n\n\t\t\t\tif ( material.wireframe === true ) {\n\n\t\t\t\t\tstate.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );\n\t\t\t\t\trenderer.setMode( _gl.LINES );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tswitch ( object.drawMode ) {\n\n\t\t\t\t\t\tcase TrianglesDrawMode:\n\t\t\t\t\t\t\trenderer.setMode( _gl.TRIANGLES );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase TriangleStripDrawMode:\n\t\t\t\t\t\t\trenderer.setMode( _gl.TRIANGLE_STRIP );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase TriangleFanDrawMode:\n\t\t\t\t\t\t\trenderer.setMode( _gl.TRIANGLE_FAN );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\n\t\t\t} else if ( object.isLine ) {\n\n\t\t\t\tvar lineWidth = material.linewidth;\n\n\t\t\t\tif ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material\n\n\t\t\t\tstate.setLineWidth( lineWidth * getTargetPixelRatio() );\n\n\t\t\t\tif ( object.isLineSegments ) {\n\n\t\t\t\t\trenderer.setMode( _gl.LINES );\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderer.setMode( _gl.LINE_STRIP );\n\n\t\t\t\t}\n\n\t\t\t} else if ( object.isPoints ) {\n\n\t\t\t\trenderer.setMode( _gl.POINTS );\n\n\t\t\t}\n\n\t\t\tif ( geometry && geometry.isInstancedBufferGeometry ) {\n\n\t\t\t\tif ( geometry.maxInstancedCount > 0 ) {\n\n\t\t\t\t\trenderer.renderInstances( geometry, drawStart, drawCount );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\trenderer.render( drawStart, drawCount );\n\n\t\t\t}\n\n\t\t};\n\n\t\tfunction setupVertexAttributes( material, program, geometry, startIndex ) {\n\n\t\t\tvar extension;\n\n\t\t\tif ( geometry && geometry.isInstancedBufferGeometry ) {\n\n\t\t\t\textension = extensions.get( 'ANGLE_instanced_arrays' );\n\n\t\t\t\tif ( extension === null ) {\n\n\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( startIndex === undefined ) startIndex = 0;\n\n\t\t\tstate.initAttributes();\n\n\t\t\tvar geometryAttributes = geometry.attributes;\n\n\t\t\tvar programAttributes = program.getAttributes();\n\n\t\t\tvar materialDefaultAttributeValues = material.defaultAttributeValues;\n\n\t\t\tfor ( var name in programAttributes ) {\n\n\t\t\t\tvar programAttribute = programAttributes[ name ];\n\n\t\t\t\tif ( programAttribute >= 0 ) {\n\n\t\t\t\t\tvar geometryAttribute = geometryAttributes[ name ];\n\n\t\t\t\t\tif ( geometryAttribute !== undefined ) {\n\n\t\t\t\t\t\tvar type = _gl.FLOAT;\n\t\t\t\t\t\tvar array = geometryAttribute.array;\n\t\t\t\t\t\tvar normalized = geometryAttribute.normalized;\n\n\t\t\t\t\t\tif ( array instanceof Float32Array ) {\n\n\t\t\t\t\t\t\ttype = _gl.FLOAT;\n\n\t\t\t\t\t\t} else if ( array instanceof Float64Array ) {\n\n\t\t\t\t\t\t\tconsole.warn( \"Unsupported data buffer format: Float64Array\" );\n\n\t\t\t\t\t\t} else if ( array instanceof Uint16Array ) {\n\n\t\t\t\t\t\t\ttype = _gl.UNSIGNED_SHORT;\n\n\t\t\t\t\t\t} else if ( array instanceof Int16Array ) {\n\n\t\t\t\t\t\t\ttype = _gl.SHORT;\n\n\t\t\t\t\t\t} else if ( array instanceof Uint32Array ) {\n\n\t\t\t\t\t\t\ttype = _gl.UNSIGNED_INT;\n\n\t\t\t\t\t\t} else if ( array instanceof Int32Array ) {\n\n\t\t\t\t\t\t\ttype = _gl.INT;\n\n\t\t\t\t\t\t} else if ( array instanceof Int8Array ) {\n\n\t\t\t\t\t\t\ttype = _gl.BYTE;\n\n\t\t\t\t\t\t} else if ( array instanceof Uint8Array ) {\n\n\t\t\t\t\t\t\ttype = _gl.UNSIGNED_BYTE;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvar size = geometryAttribute.itemSize;\n\t\t\t\t\t\tvar buffer = objects.getAttributeBuffer( geometryAttribute );\n\n\t\t\t\t\t\tif ( geometryAttribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\t\t\t\tvar data = geometryAttribute.data;\n\t\t\t\t\t\t\tvar stride = data.stride;\n\t\t\t\t\t\t\tvar offset = geometryAttribute.offset;\n\n\t\t\t\t\t\t\tif ( data && data.isInstancedInterleavedBuffer ) {\n\n\t\t\t\t\t\t\t\tstate.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension );\n\n\t\t\t\t\t\t\t\tif ( geometry.maxInstancedCount === undefined ) {\n\n\t\t\t\t\t\t\t\t\tgeometry.maxInstancedCount = data.meshPerAttribute * data.count;\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.enableAttribute( programAttribute );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );\n\t\t\t\t\t\t\t_gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( geometryAttribute.isInstancedBufferAttribute ) {\n\n\t\t\t\t\t\t\t\tstate.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension );\n\n\t\t\t\t\t\t\t\tif ( geometry.maxInstancedCount === undefined ) {\n\n\t\t\t\t\t\t\t\t\tgeometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.enableAttribute( programAttribute );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );\n\t\t\t\t\t\t\t_gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * geometryAttribute.array.BYTES_PER_ELEMENT );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if ( materialDefaultAttributeValues !== undefined ) {\n\n\t\t\t\t\t\tvar value = materialDefaultAttributeValues[ name ];\n\n\t\t\t\t\t\tif ( value !== undefined ) {\n\n\t\t\t\t\t\t\tswitch ( value.length ) {\n\n\t\t\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\t\t\t_gl.vertexAttrib2fv( programAttribute, value );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\t\t\t_gl.vertexAttrib3fv( programAttribute, value );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase 4:\n\t\t\t\t\t\t\t\t\t_gl.vertexAttrib4fv( programAttribute, value );\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t_gl.vertexAttrib1fv( programAttribute, value );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.disableUnusedAttributes();\n\n\t\t}\n\n\t\t// Sorting\n\n\t\tfunction absNumericalSort( a, b ) {\n\n\t\t\treturn Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] );\n\n\t\t}\n\n\t\tfunction painterSortStable( a, b ) {\n\n\t\t\tif ( a.object.renderOrder !== b.object.renderOrder ) {\n\n\t\t\t\treturn a.object.renderOrder - b.object.renderOrder;\n\n\t\t\t} else if ( a.material.program && b.material.program && a.material.program !== b.material.program ) {\n\n\t\t\t\treturn a.material.program.id - b.material.program.id;\n\n\t\t\t} else if ( a.material.id !== b.material.id ) {\n\n\t\t\t\treturn a.material.id - b.material.id;\n\n\t\t\t} else if ( a.z !== b.z ) {\n\n\t\t\t\treturn a.z - b.z;\n\n\t\t\t} else {\n\n\t\t\t\treturn a.id - b.id;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction reversePainterSortStable( a, b ) {\n\n\t\t\tif ( a.object.renderOrder !== b.object.renderOrder ) {\n\n\t\t\t\treturn a.object.renderOrder - b.object.renderOrder;\n\n\t\t\t} if ( a.z !== b.z ) {\n\n\t\t\t\treturn b.z - a.z;\n\n\t\t\t} else {\n\n\t\t\t\treturn a.id - b.id;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Rendering\n\n\t\tthis.render = function ( scene, camera, renderTarget, forceClear ) {\n\n\t\t\tif ( camera !== undefined && camera.isCamera !== true ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\t// reset caching for this frame\n\n\t\t\t_currentGeometryProgram = '';\n\t\t\t_currentMaterialId = - 1;\n\t\t\t_currentCamera = null;\n\n\t\t\t// update scene graph\n\n\t\t\tif ( scene.autoUpdate === true ) scene.updateMatrixWorld();\n\n\t\t\t// update camera matrices and frustum\n\n\t\t\tif ( camera.parent === null ) camera.updateMatrixWorld();\n\n\t\t\tcamera.matrixWorldInverse.getInverse( camera.matrixWorld );\n\n\t\t\t_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );\n\t\t\t_frustum.setFromMatrix( _projScreenMatrix );\n\n\t\t\tlights.length = 0;\n\n\t\t\topaqueObjectsLastIndex = - 1;\n\t\t\ttransparentObjectsLastIndex = - 1;\n\n\t\t\tsprites.length = 0;\n\t\t\tlensFlares.length = 0;\n\n\t\t\t_localClippingEnabled = this.localClippingEnabled;\n\t\t\t_clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );\n\n\t\t\tprojectObject( scene, camera );\n\n\t\t\topaqueObjects.length = opaqueObjectsLastIndex + 1;\n\t\t\ttransparentObjects.length = transparentObjectsLastIndex + 1;\n\n\t\t\tif ( _this.sortObjects === true ) {\n\n\t\t\t\topaqueObjects.sort( painterSortStable );\n\t\t\t\ttransparentObjects.sort( reversePainterSortStable );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( _clippingEnabled ) _clipping.beginShadows();\n\n\t\t\tsetupShadows( lights );\n\n\t\t\tshadowMap.render( scene, camera );\n\n\t\t\tsetupLights( lights, camera );\n\n\t\t\tif ( _clippingEnabled ) _clipping.endShadows();\n\n\t\t\t//\n\n\t\t\t_infoRender.calls = 0;\n\t\t\t_infoRender.vertices = 0;\n\t\t\t_infoRender.faces = 0;\n\t\t\t_infoRender.points = 0;\n\n\t\t\tif ( renderTarget === undefined ) {\n\n\t\t\t\trenderTarget = null;\n\n\t\t\t}\n\n\t\t\tthis.setRenderTarget( renderTarget );\n\n\t\t\t//\n\n\t\t\tvar background = scene.background;\n\n\t\t\tif ( background === null ) {\n\n\t\t\t\tglClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );\n\n\t\t\t} else if ( background && background.isColor ) {\n\n\t\t\t\tglClearColor( background.r, background.g, background.b, 1 );\n\t\t\t\tforceClear = true;\n\n\t\t\t}\n\n\t\t\tif ( this.autoClear || forceClear ) {\n\n\t\t\t\tthis.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );\n\n\t\t\t}\n\n\t\t\tif ( background && background.isCubeTexture ) {\n\n\t\t\t\tbackgroundCamera2.projectionMatrix.copy( camera.projectionMatrix );\n\n\t\t\t\tbackgroundCamera2.matrixWorld.extractRotation( camera.matrixWorld );\n\t\t\t\tbackgroundCamera2.matrixWorldInverse.getInverse( backgroundCamera2.matrixWorld );\n\n\t\t\t\tbackgroundBoxMesh.material.uniforms[ \"tCube\" ].value = background;\n\t\t\t\tbackgroundBoxMesh.modelViewMatrix.multiplyMatrices( backgroundCamera2.matrixWorldInverse, backgroundBoxMesh.matrixWorld );\n\n\t\t\t\tobjects.update( backgroundBoxMesh );\n\n\t\t\t\t_this.renderBufferDirect( backgroundCamera2, null, backgroundBoxMesh.geometry, backgroundBoxMesh.material, backgroundBoxMesh, null );\n\n\t\t\t} else if ( background && background.isTexture ) {\n\n\t\t\t\tbackgroundPlaneMesh.material.map = background;\n\n\t\t\t\tobjects.update( backgroundPlaneMesh );\n\n\t\t\t\t_this.renderBufferDirect( backgroundCamera, null, backgroundPlaneMesh.geometry, backgroundPlaneMesh.material, backgroundPlaneMesh, null );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( scene.overrideMaterial ) {\n\n\t\t\t\tvar overrideMaterial = scene.overrideMaterial;\n\n\t\t\t\trenderObjects( opaqueObjects, scene, camera, overrideMaterial );\n\t\t\t\trenderObjects( transparentObjects, scene, camera, overrideMaterial );\n\n\t\t\t} else {\n\n\t\t\t\t// opaque pass (front-to-back order)\n\n\t\t\t\tstate.setBlending( NoBlending );\n\t\t\t\trenderObjects( opaqueObjects, scene, camera );\n\n\t\t\t\t// transparent pass (back-to-front order)\n\n\t\t\t\trenderObjects( transparentObjects, scene, camera );\n\n\t\t\t}\n\n\t\t\t// custom render plugins (post pass)\n\n\t\t\tspritePlugin.render( scene, camera );\n\t\t\tlensFlarePlugin.render( scene, camera, _currentViewport );\n\n\t\t\t// Generate mipmap if we're using any kind of mipmap filtering\n\n\t\t\tif ( renderTarget ) {\n\n\t\t\t\ttextures.updateRenderTargetMipmap( renderTarget );\n\n\t\t\t}\n\n\t\t\t// Ensure depth buffer writing is enabled so it can be cleared on next render\n\n\t\t\tstate.setDepthTest( true );\n\t\t\tstate.setDepthWrite( true );\n\t\t\tstate.setColorWrite( true );\n\n\t\t\t// _gl.finish();\n\n\t\t};\n\n\t\tfunction pushRenderItem( object, geometry, material, z, group ) {\n\n\t\t\tvar array, index;\n\n\t\t\t// allocate the next position in the appropriate array\n\n\t\t\tif ( material.transparent ) {\n\n\t\t\t\tarray = transparentObjects;\n\t\t\t\tindex = ++ transparentObjectsLastIndex;\n\n\t\t\t} else {\n\n\t\t\t\tarray = opaqueObjects;\n\t\t\t\tindex = ++ opaqueObjectsLastIndex;\n\n\t\t\t}\n\n\t\t\t// recycle existing render item or grow the array\n\n\t\t\tvar renderItem = array[ index ];\n\n\t\t\tif ( renderItem !== undefined ) {\n\n\t\t\t\trenderItem.id = object.id;\n\t\t\t\trenderItem.object = object;\n\t\t\t\trenderItem.geometry = geometry;\n\t\t\t\trenderItem.material = material;\n\t\t\t\trenderItem.z = _vector3.z;\n\t\t\t\trenderItem.group = group;\n\n\t\t\t} else {\n\n\t\t\t\trenderItem = {\n\t\t\t\t\tid: object.id,\n\t\t\t\t\tobject: object,\n\t\t\t\t\tgeometry: geometry,\n\t\t\t\t\tmaterial: material,\n\t\t\t\t\tz: _vector3.z,\n\t\t\t\t\tgroup: group\n\t\t\t\t};\n\n\t\t\t\t// assert( index === array.length );\n\t\t\t\tarray.push( renderItem );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// TODO Duplicated code (Frustum)\n\n\t\tfunction isObjectViewable( object ) {\n\n\t\t\tvar geometry = object.geometry;\n\n\t\t\tif ( geometry.boundingSphere === null )\n\t\t\t\tgeometry.computeBoundingSphere();\n\n\t\t\t_sphere.copy( geometry.boundingSphere ).\n\t\t\t\tapplyMatrix4( object.matrixWorld );\n\n\t\t\treturn isSphereViewable( _sphere );\n\n\t\t}\n\n\t\tfunction isSpriteViewable( sprite ) {\n\n\t\t\t_sphere.center.set( 0, 0, 0 );\n\t\t\t_sphere.radius = 0.7071067811865476;\n\t\t\t_sphere.applyMatrix4( sprite.matrixWorld );\n\n\t\t\treturn isSphereViewable( _sphere );\n\n\t\t}\n\n\t\tfunction isSphereViewable( sphere ) {\n\n\t\t\tif ( ! _frustum.intersectsSphere( sphere ) ) return false;\n\n\t\t\tvar numPlanes = _clipping.numPlanes;\n\n\t\t\tif ( numPlanes === 0 ) return true;\n\n\t\t\tvar planes = _this.clippingPlanes,\n\n\t\t\t\tcenter = sphere.center,\n\t\t\t\tnegRad = - sphere.radius,\n\t\t\t\ti = 0;\n\n\t\t\tdo {\n\n\t\t\t\t// out when deeper than radius in the negative halfspace\n\t\t\t\tif ( planes[ i ].distanceToPoint( center ) < negRad ) return false;\n\n\t\t\t} while ( ++ i !== numPlanes );\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\tfunction projectObject( object, camera ) {\n\n\t\t\tif ( object.visible === false ) return;\n\n\t\t\tvar visible = ( object.layers.mask & camera.layers.mask ) !== 0;\n\n\t\t\tif ( visible ) {\n\n\t\t\t\tif ( object.isLight ) {\n\n\t\t\t\t\tlights.push( object );\n\n\t\t\t\t} else if ( object.isSprite ) {\n\n\t\t\t\t\tif ( object.frustumCulled === false || isSpriteViewable( object ) === true ) {\n\n\t\t\t\t\t\tsprites.push( object );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isLensFlare ) {\n\n\t\t\t\t\tlensFlares.push( object );\n\n\t\t\t\t} else if ( object.isImmediateRenderObject ) {\n\n\t\t\t\t\tif ( _this.sortObjects === true ) {\n\n\t\t\t\t\t\t_vector3.setFromMatrixPosition( object.matrixWorld );\n\t\t\t\t\t\t_vector3.applyProjection( _projScreenMatrix );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tpushRenderItem( object, null, object.material, _vector3.z, null );\n\n\t\t\t\t} else if ( object.isMesh || object.isLine || object.isPoints ) {\n\n\t\t\t\t\tif ( object.isSkinnedMesh ) {\n\n\t\t\t\t\t\tobject.skeleton.update();\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( object.frustumCulled === false || isObjectViewable( object ) === true ) {\n\n\t\t\t\t\t\tvar material = object.material;\n\n\t\t\t\t\t\tif ( material.visible === true ) {\n\n\t\t\t\t\t\t\tif ( _this.sortObjects === true ) {\n\n\t\t\t\t\t\t\t\t_vector3.setFromMatrixPosition( object.matrixWorld );\n\t\t\t\t\t\t\t\t_vector3.applyProjection( _projScreenMatrix );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tvar geometry = objects.update( object );\n\n\t\t\t\t\t\t\tif ( material.isMultiMaterial ) {\n\n\t\t\t\t\t\t\t\tvar groups = geometry.groups;\n\t\t\t\t\t\t\t\tvar materials = material.materials;\n\n\t\t\t\t\t\t\t\tfor ( var i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\t\t\t\t\t\t\tvar group = groups[ i ];\n\t\t\t\t\t\t\t\t\tvar groupMaterial = materials[ group.materialIndex ];\n\n\t\t\t\t\t\t\t\t\tif ( groupMaterial.visible === true ) {\n\n\t\t\t\t\t\t\t\t\t\tpushRenderItem( object, geometry, groupMaterial, _vector3.z, group );\n\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tpushRenderItem( object, geometry, material, _vector3.z, null );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar children = object.children;\n\n\t\t\tfor ( var i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tprojectObject( children[ i ], camera );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction renderObjects( renderList, scene, camera, overrideMaterial ) {\n\n\t\t\tfor ( var i = 0, l = renderList.length; i < l; i ++ ) {\n\n\t\t\t\tvar renderItem = renderList[ i ];\n\n\t\t\t\tvar object = renderItem.object;\n\t\t\t\tvar geometry = renderItem.geometry;\n\t\t\t\tvar material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;\n\t\t\t\tvar group = renderItem.group;\n\n\t\t\t\tobject.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );\n\t\t\t\tobject.normalMatrix.getNormalMatrix( object.modelViewMatrix );\n\n\t\t\t\tobject.onBeforeRender( _this, scene, camera, geometry, material, group );\n\n\t\t\t\tif ( object.isImmediateRenderObject ) {\n\n\t\t\t\t\tsetMaterial( material );\n\n\t\t\t\t\tvar program = setProgram( camera, scene.fog, material, object );\n\n\t\t\t\t\t_currentGeometryProgram = '';\n\n\t\t\t\t\tobject.render( function ( object ) {\n\n\t\t\t\t\t\t_this.renderBufferImmediate( object, program, material );\n\n\t\t\t\t\t} );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_this.renderBufferDirect( camera, scene.fog, geometry, material, object, group );\n\n\t\t\t\t}\n\n\t\t\t\tobject.onAfterRender( _this, scene, camera, geometry, material, group );\n\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction initMaterial( material, fog, object ) {\n\n\t\t\tvar materialProperties = properties.get( material );\n\n\t\t\tvar parameters = programCache.getParameters(\n\t\t\t\t\tmaterial, _lights, fog, _clipping.numPlanes, _clipping.numIntersection, object );\n\n\t\t\tvar code = programCache.getProgramCode( material, parameters );\n\n\t\t\tvar program = materialProperties.program;\n\t\t\tvar programChange = true;\n\n\t\t\tif ( program === undefined ) {\n\n\t\t\t\t// new material\n\t\t\t\tmaterial.addEventListener( 'dispose', onMaterialDispose );\n\n\t\t\t} else if ( program.code !== code ) {\n\n\t\t\t\t// changed glsl or parameters\n\t\t\t\treleaseMaterialProgramReference( material );\n\n\t\t\t} else if ( parameters.shaderID !== undefined ) {\n\n\t\t\t\t// same glsl and uniform list\n\t\t\t\treturn;\n\n\t\t\t} else {\n\n\t\t\t\t// only rebuild uniform list\n\t\t\t\tprogramChange = false;\n\n\t\t\t}\n\n\t\t\tif ( programChange ) {\n\n\t\t\t\tif ( parameters.shaderID ) {\n\n\t\t\t\t\tvar shader = ShaderLib[ parameters.shaderID ];\n\n\t\t\t\t\tmaterialProperties.__webglShader = {\n\t\t\t\t\t\tname: material.type,\n\t\t\t\t\t\tuniforms: UniformsUtils.clone( shader.uniforms ),\n\t\t\t\t\t\tvertexShader: shader.vertexShader,\n\t\t\t\t\t\tfragmentShader: shader.fragmentShader\n\t\t\t\t\t};\n\n\t\t\t\t} else {\n\n\t\t\t\t\tmaterialProperties.__webglShader = {\n\t\t\t\t\t\tname: material.type,\n\t\t\t\t\t\tuniforms: material.uniforms,\n\t\t\t\t\t\tvertexShader: material.vertexShader,\n\t\t\t\t\t\tfragmentShader: material.fragmentShader\n\t\t\t\t\t};\n\n\t\t\t\t}\n\n\t\t\t\tmaterial.__webglShader = materialProperties.__webglShader;\n\n\t\t\t\tprogram = programCache.acquireProgram( material, parameters, code );\n\n\t\t\t\tmaterialProperties.program = program;\n\t\t\t\tmaterial.program = program;\n\n\t\t\t}\n\n\t\t\tvar attributes = program.getAttributes();\n\n\t\t\tif ( material.morphTargets ) {\n\n\t\t\t\tmaterial.numSupportedMorphTargets = 0;\n\n\t\t\t\tfor ( var i = 0; i < _this.maxMorphTargets; i ++ ) {\n\n\t\t\t\t\tif ( attributes[ 'morphTarget' + i ] >= 0 ) {\n\n\t\t\t\t\t\tmaterial.numSupportedMorphTargets ++;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( material.morphNormals ) {\n\n\t\t\t\tmaterial.numSupportedMorphNormals = 0;\n\n\t\t\t\tfor ( var i = 0; i < _this.maxMorphNormals; i ++ ) {\n\n\t\t\t\t\tif ( attributes[ 'morphNormal' + i ] >= 0 ) {\n\n\t\t\t\t\t\tmaterial.numSupportedMorphNormals ++;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar uniforms = materialProperties.__webglShader.uniforms;\n\n\t\t\tif ( ! material.isShaderMaterial &&\n\t\t\t ! material.isRawShaderMaterial ||\n\t\t\t material.clipping === true ) {\n\n\t\t\t\tmaterialProperties.numClippingPlanes = _clipping.numPlanes;\n\t\t\t\tmaterialProperties.numIntersection = _clipping.numIntersection;\n\t\t\t\tuniforms.clippingPlanes = _clipping.uniform;\n\n\t\t\t}\n\n\t\t\tmaterialProperties.fog = fog;\n\n\t\t\t// store the light setup it was created for\n\n\t\t\tmaterialProperties.lightsHash = _lights.hash;\n\n\t\t\tif ( material.lights ) {\n\n\t\t\t\t// wire up the material to this renderer's lighting state\n\n\t\t\t\tuniforms.ambientLightColor.value = _lights.ambient;\n\t\t\t\tuniforms.directionalLights.value = _lights.directional;\n\t\t\t\tuniforms.spotLights.value = _lights.spot;\n\t\t\t\tuniforms.pointLights.value = _lights.point;\n\t\t\t\tuniforms.hemisphereLights.value = _lights.hemi;\n\n\t\t\t\tuniforms.directionalShadowMap.value = _lights.directionalShadowMap;\n\t\t\t\tuniforms.directionalShadowMatrix.value = _lights.directionalShadowMatrix;\n\t\t\t\tuniforms.spotShadowMap.value = _lights.spotShadowMap;\n\t\t\t\tuniforms.spotShadowMatrix.value = _lights.spotShadowMatrix;\n\t\t\t\tuniforms.pointShadowMap.value = _lights.pointShadowMap;\n\t\t\t\tuniforms.pointShadowMatrix.value = _lights.pointShadowMatrix;\n\n\t\t\t}\n\n\t\t\tvar progUniforms = materialProperties.program.getUniforms(),\n\t\t\t\tuniformsList =\n\t\t\t\t\t\tWebGLUniforms.seqWithValue( progUniforms.seq, uniforms );\n\n\t\t\tmaterialProperties.uniformsList = uniformsList;\n\n\t\t}\n\n\t\tfunction setMaterial( material ) {\n\n\t\t\tmaterial.side === DoubleSide\n\t\t\t\t? state.disable( _gl.CULL_FACE )\n\t\t\t\t: state.enable( _gl.CULL_FACE );\n\n\t\t\tstate.setFlipSided( material.side === BackSide );\n\n\t\t\tmaterial.transparent === true\n\t\t\t\t? state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha )\n\t\t\t\t: state.setBlending( NoBlending );\n\n\t\t\tstate.setDepthFunc( material.depthFunc );\n\t\t\tstate.setDepthTest( material.depthTest );\n\t\t\tstate.setDepthWrite( material.depthWrite );\n\t\t\tstate.setColorWrite( material.colorWrite );\n\t\t\tstate.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );\n\n\t\t}\n\n\t\tfunction setProgram( camera, fog, material, object ) {\n\n\t\t\t_usedTextureUnits = 0;\n\n\t\t\tvar materialProperties = properties.get( material );\n\n\t\t\tif ( _clippingEnabled ) {\n\n\t\t\t\tif ( _localClippingEnabled || camera !== _currentCamera ) {\n\n\t\t\t\t\tvar useCache =\n\t\t\t\t\t\t\tcamera === _currentCamera &&\n\t\t\t\t\t\t\tmaterial.id === _currentMaterialId;\n\n\t\t\t\t\t// we might want to call this function with some ClippingGroup\n\t\t\t\t\t// object instead of the material, once it becomes feasible\n\t\t\t\t\t// (#8465, #8379)\n\t\t\t\t\t_clipping.setState(\n\t\t\t\t\t\t\tmaterial.clippingPlanes, material.clipIntersection, material.clipShadows,\n\t\t\t\t\t\t\tcamera, materialProperties, useCache );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( material.needsUpdate === false ) {\n\n\t\t\t\tif ( materialProperties.program === undefined ) {\n\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t} else if ( material.fog && materialProperties.fog !== fog ) {\n\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t} else if ( material.lights && materialProperties.lightsHash !== _lights.hash ) {\n\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t} else if ( materialProperties.numClippingPlanes !== undefined &&\n\t\t\t\t\t( materialProperties.numClippingPlanes !== _clipping.numPlanes || \n\t \t\t\t\t materialProperties.numIntersection !== _clipping.numIntersection ) ) {\n\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( material.needsUpdate ) {\n\n\t\t\t\tinitMaterial( material, fog, object );\n\t\t\t\tmaterial.needsUpdate = false;\n\n\t\t\t}\n\n\t\t\tvar refreshProgram = false;\n\t\t\tvar refreshMaterial = false;\n\t\t\tvar refreshLights = false;\n\n\t\t\tvar program = materialProperties.program,\n\t\t\t\tp_uniforms = program.getUniforms(),\n\t\t\t\tm_uniforms = materialProperties.__webglShader.uniforms;\n\n\t\t\tif ( program.id !== _currentProgram ) {\n\n\t\t\t\t_gl.useProgram( program.program );\n\t\t\t\t_currentProgram = program.id;\n\n\t\t\t\trefreshProgram = true;\n\t\t\t\trefreshMaterial = true;\n\t\t\t\trefreshLights = true;\n\n\t\t\t}\n\n\t\t\tif ( material.id !== _currentMaterialId ) {\n\n\t\t\t\t_currentMaterialId = material.id;\n\n\t\t\t\trefreshMaterial = true;\n\n\t\t\t}\n\n\t\t\tif ( refreshProgram || camera !== _currentCamera ) {\n\n\t\t\t\tp_uniforms.set( _gl, camera, 'projectionMatrix' );\n\n\t\t\t\tif ( capabilities.logarithmicDepthBuffer ) {\n\n\t\t\t\t\tp_uniforms.setValue( _gl, 'logDepthBufFC',\n\t\t\t\t\t\t\t2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );\n\n\t\t\t\t}\n\n\n\t\t\t\tif ( camera !== _currentCamera ) {\n\n\t\t\t\t\t_currentCamera = camera;\n\n\t\t\t\t\t// lighting uniforms depend on the camera so enforce an update\n\t\t\t\t\t// now, in case this material supports lights - or later, when\n\t\t\t\t\t// the next material that does gets activated:\n\n\t\t\t\t\trefreshMaterial = true;\t\t// set to true on material change\n\t\t\t\t\trefreshLights = true;\t\t// remains set until update done\n\n\t\t\t\t}\n\n\t\t\t\t// load material specific uniforms\n\t\t\t\t// (shader material also gets them for the sake of genericity)\n\n\t\t\t\tif ( material.isShaderMaterial ||\n\t\t\t\t material.isMeshPhongMaterial ||\n\t\t\t\t material.isMeshStandardMaterial ||\n\t\t\t\t material.envMap ) {\n\n\t\t\t\t\tvar uCamPos = p_uniforms.map.cameraPosition;\n\n\t\t\t\t\tif ( uCamPos !== undefined ) {\n\n\t\t\t\t\t\tuCamPos.setValue( _gl,\n\t\t\t\t\t\t\t\t_vector3.setFromMatrixPosition( camera.matrixWorld ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( material.isMeshPhongMaterial ||\n\t\t\t\t material.isMeshLambertMaterial ||\n\t\t\t\t material.isMeshBasicMaterial ||\n\t\t\t\t material.isMeshStandardMaterial ||\n\t\t\t\t material.isShaderMaterial ||\n\t\t\t\t material.skinning ) {\n\n\t\t\t\t\tp_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );\n\n\t\t\t\t}\n\n\t\t\t\tp_uniforms.set( _gl, _this, 'toneMappingExposure' );\n\t\t\t\tp_uniforms.set( _gl, _this, 'toneMappingWhitePoint' );\n\n\t\t\t}\n\n\t\t\t// skinning uniforms must be set even if material didn't change\n\t\t\t// auto-setting of texture unit for bone texture must go before other textures\n\t\t\t// not sure why, but otherwise weird things happen\n\n\t\t\tif ( material.skinning ) {\n\n\t\t\t\tp_uniforms.setOptional( _gl, object, 'bindMatrix' );\n\t\t\t\tp_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );\n\n\t\t\t\tvar skeleton = object.skeleton;\n\n\t\t\t\tif ( skeleton ) {\n\n\t\t\t\t\tif ( capabilities.floatVertexTextures && skeleton.useVertexTexture ) {\n\n\t\t\t\t\t\tp_uniforms.set( _gl, skeleton, 'boneTexture' );\n\t\t\t\t\t\tp_uniforms.set( _gl, skeleton, 'boneTextureWidth' );\n\t\t\t\t\t\tp_uniforms.set( _gl, skeleton, 'boneTextureHeight' );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tp_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( refreshMaterial ) {\n\n\t\t\t\tif ( material.lights ) {\n\n\t\t\t\t\t// the current material requires lighting info\n\n\t\t\t\t\t// note: all lighting uniforms are always set correctly\n\t\t\t\t\t// they simply reference the renderer's state for their\n\t\t\t\t\t// values\n\t\t\t\t\t//\n\t\t\t\t\t// use the current material's .needsUpdate flags to set\n\t\t\t\t\t// the GL state when required\n\n\t\t\t\t\tmarkUniformsLightsNeedsUpdate( m_uniforms, refreshLights );\n\n\t\t\t\t}\n\n\t\t\t\t// refresh uniforms common to several materials\n\n\t\t\t\tif ( fog && material.fog ) {\n\n\t\t\t\t\trefreshUniformsFog( m_uniforms, fog );\n\n\t\t\t\t}\n\n\t\t\t\tif ( material.isMeshBasicMaterial ||\n\t\t\t\t material.isMeshLambertMaterial ||\n\t\t\t\t material.isMeshPhongMaterial ||\n\t\t\t\t material.isMeshStandardMaterial ||\n\t\t\t\t material.isMeshDepthMaterial ) {\n\n\t\t\t\t\trefreshUniformsCommon( m_uniforms, material );\n\n\t\t\t\t}\n\n\t\t\t\t// refresh single material specific uniforms\n\n\t\t\t\tif ( material.isLineBasicMaterial ) {\n\n\t\t\t\t\trefreshUniformsLine( m_uniforms, material );\n\n\t\t\t\t} else if ( material.isLineDashedMaterial ) {\n\n\t\t\t\t\trefreshUniformsLine( m_uniforms, material );\n\t\t\t\t\trefreshUniformsDash( m_uniforms, material );\n\n\t\t\t\t} else if ( material.isPointsMaterial ) {\n\n\t\t\t\t\trefreshUniformsPoints( m_uniforms, material );\n\n\t\t\t\t} else if ( material.isMeshLambertMaterial ) {\n\n\t\t\t\t\trefreshUniformsLambert( m_uniforms, material );\n\n\t\t\t\t} else if ( material.isMeshPhongMaterial ) {\n\n\t\t\t\t\trefreshUniformsPhong( m_uniforms, material );\n\n\t\t\t\t} else if ( material.isMeshPhysicalMaterial ) {\n\n\t\t\t\t\trefreshUniformsPhysical( m_uniforms, material );\n\n\t\t\t\t} else if ( material.isMeshStandardMaterial ) {\n\n\t\t\t\t\trefreshUniformsStandard( m_uniforms, material );\n\n\t\t\t\t} else if ( material.isMeshDepthMaterial ) {\n\n\t\t\t\t\tif ( material.displacementMap ) {\n\n\t\t\t\t\t\tm_uniforms.displacementMap.value = material.displacementMap;\n\t\t\t\t\t\tm_uniforms.displacementScale.value = material.displacementScale;\n\t\t\t\t\t\tm_uniforms.displacementBias.value = material.displacementBias;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( material.isMeshNormalMaterial ) {\n\n\t\t\t\t\tm_uniforms.opacity.value = material.opacity;\n\n\t\t\t\t}\n\n\t\t\t\tWebGLUniforms.upload(\n\t\t\t\t\t\t_gl, materialProperties.uniformsList, m_uniforms, _this );\n\n\t\t\t}\n\n\n\t\t\t// common matrices\n\n\t\t\tp_uniforms.set( _gl, object, 'modelViewMatrix' );\n\t\t\tp_uniforms.set( _gl, object, 'normalMatrix' );\n\t\t\tp_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );\n\n\t\t\treturn program;\n\n\t\t}\n\n\t\t// Uniforms (refresh uniforms objects)\n\n\t\tfunction refreshUniformsCommon( uniforms, material ) {\n\n\t\t\tuniforms.opacity.value = material.opacity;\n\n\t\t\tuniforms.diffuse.value = material.color;\n\n\t\t\tif ( material.emissive ) {\n\n\t\t\t\tuniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );\n\n\t\t\t}\n\n\t\t\tuniforms.map.value = material.map;\n\t\t\tuniforms.specularMap.value = material.specularMap;\n\t\t\tuniforms.alphaMap.value = material.alphaMap;\n\n\t\t\tif ( material.aoMap ) {\n\n\t\t\t\tuniforms.aoMap.value = material.aoMap;\n\t\t\t\tuniforms.aoMapIntensity.value = material.aoMapIntensity;\n\n\t\t\t}\n\n\t\t\t// uv repeat and offset setting priorities\n\t\t\t// 1. color map\n\t\t\t// 2. specular map\n\t\t\t// 3. normal map\n\t\t\t// 4. bump map\n\t\t\t// 5. alpha map\n\t\t\t// 6. emissive map\n\n\t\t\tvar uvScaleMap;\n\n\t\t\tif ( material.map ) {\n\n\t\t\t\tuvScaleMap = material.map;\n\n\t\t\t} else if ( material.specularMap ) {\n\n\t\t\t\tuvScaleMap = material.specularMap;\n\n\t\t\t} else if ( material.displacementMap ) {\n\n\t\t\t\tuvScaleMap = material.displacementMap;\n\n\t\t\t} else if ( material.normalMap ) {\n\n\t\t\t\tuvScaleMap = material.normalMap;\n\n\t\t\t} else if ( material.bumpMap ) {\n\n\t\t\t\tuvScaleMap = material.bumpMap;\n\n\t\t\t} else if ( material.roughnessMap ) {\n\n\t\t\t\tuvScaleMap = material.roughnessMap;\n\n\t\t\t} else if ( material.metalnessMap ) {\n\n\t\t\t\tuvScaleMap = material.metalnessMap;\n\n\t\t\t} else if ( material.alphaMap ) {\n\n\t\t\t\tuvScaleMap = material.alphaMap;\n\n\t\t\t} else if ( material.emissiveMap ) {\n\n\t\t\t\tuvScaleMap = material.emissiveMap;\n\n\t\t\t}\n\n\t\t\tif ( uvScaleMap !== undefined ) {\n\n\t\t\t\t// backwards compatibility\n\t\t\t\tif ( uvScaleMap.isWebGLRenderTarget ) {\n\n\t\t\t\t\tuvScaleMap = uvScaleMap.texture;\n\n\t\t\t\t}\n\n\t\t\t\tvar offset = uvScaleMap.offset;\n\t\t\t\tvar repeat = uvScaleMap.repeat;\n\n\t\t\t\tuniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );\n\n\t\t\t}\n\n\t\t\tuniforms.envMap.value = material.envMap;\n\n\t\t\t// don't flip CubeTexture envMaps, flip everything else:\n\t\t\t// WebGLRenderTargetCube will be flipped for backwards compatibility\n\t\t\t// WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture\n\t\t\t// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future\n\t\t\tuniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;\n\n\t\t\tuniforms.reflectivity.value = material.reflectivity;\n\t\t\tuniforms.refractionRatio.value = material.refractionRatio;\n\n\t\t}\n\n\t\tfunction refreshUniformsLine( uniforms, material ) {\n\n\t\t\tuniforms.diffuse.value = material.color;\n\t\t\tuniforms.opacity.value = material.opacity;\n\n\t\t}\n\n\t\tfunction refreshUniformsDash( uniforms, material ) {\n\n\t\t\tuniforms.dashSize.value = material.dashSize;\n\t\t\tuniforms.totalSize.value = material.dashSize + material.gapSize;\n\t\t\tuniforms.scale.value = material.scale;\n\n\t\t}\n\n\t\tfunction refreshUniformsPoints( uniforms, material ) {\n\n\t\t\tuniforms.diffuse.value = material.color;\n\t\t\tuniforms.opacity.value = material.opacity;\n\t\t\tuniforms.size.value = material.size * _pixelRatio;\n\t\t\tuniforms.scale.value = _height * 0.5;\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\tif ( material.map !== null ) {\n\n\t\t\t\tvar offset = material.map.offset;\n\t\t\t\tvar repeat = material.map.repeat;\n\n\t\t\t\tuniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction refreshUniformsFog( uniforms, fog ) {\n\n\t\t\tuniforms.fogColor.value = fog.color;\n\n\t\t\tif ( fog.isFog ) {\n\n\t\t\t\tuniforms.fogNear.value = fog.near;\n\t\t\t\tuniforms.fogFar.value = fog.far;\n\n\t\t\t} else if ( fog.isFogExp2 ) {\n\n\t\t\t\tuniforms.fogDensity.value = fog.density;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction refreshUniformsLambert( uniforms, material ) {\n\n\t\t\tif ( material.lightMap ) {\n\n\t\t\t\tuniforms.lightMap.value = material.lightMap;\n\t\t\t\tuniforms.lightMapIntensity.value = material.lightMapIntensity;\n\n\t\t\t}\n\n\t\t\tif ( material.emissiveMap ) {\n\n\t\t\t\tuniforms.emissiveMap.value = material.emissiveMap;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction refreshUniformsPhong( uniforms, material ) {\n\n\t\t\tuniforms.specular.value = material.specular;\n\t\t\tuniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )\n\n\t\t\tif ( material.lightMap ) {\n\n\t\t\t\tuniforms.lightMap.value = material.lightMap;\n\t\t\t\tuniforms.lightMapIntensity.value = material.lightMapIntensity;\n\n\t\t\t}\n\n\t\t\tif ( material.emissiveMap ) {\n\n\t\t\t\tuniforms.emissiveMap.value = material.emissiveMap;\n\n\t\t\t}\n\n\t\t\tif ( material.bumpMap ) {\n\n\t\t\t\tuniforms.bumpMap.value = material.bumpMap;\n\t\t\t\tuniforms.bumpScale.value = material.bumpScale;\n\n\t\t\t}\n\n\t\t\tif ( material.normalMap ) {\n\n\t\t\t\tuniforms.normalMap.value = material.normalMap;\n\t\t\t\tuniforms.normalScale.value.copy( material.normalScale );\n\n\t\t\t}\n\n\t\t\tif ( material.displacementMap ) {\n\n\t\t\t\tuniforms.displacementMap.value = material.displacementMap;\n\t\t\t\tuniforms.displacementScale.value = material.displacementScale;\n\t\t\t\tuniforms.displacementBias.value = material.displacementBias;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction refreshUniformsStandard( uniforms, material ) {\n\n\t\t\tuniforms.roughness.value = material.roughness;\n\t\t\tuniforms.metalness.value = material.metalness;\n\n\t\t\tif ( material.roughnessMap ) {\n\n\t\t\t\tuniforms.roughnessMap.value = material.roughnessMap;\n\n\t\t\t}\n\n\t\t\tif ( material.metalnessMap ) {\n\n\t\t\t\tuniforms.metalnessMap.value = material.metalnessMap;\n\n\t\t\t}\n\n\t\t\tif ( material.lightMap ) {\n\n\t\t\t\tuniforms.lightMap.value = material.lightMap;\n\t\t\t\tuniforms.lightMapIntensity.value = material.lightMapIntensity;\n\n\t\t\t}\n\n\t\t\tif ( material.emissiveMap ) {\n\n\t\t\t\tuniforms.emissiveMap.value = material.emissiveMap;\n\n\t\t\t}\n\n\t\t\tif ( material.bumpMap ) {\n\n\t\t\t\tuniforms.bumpMap.value = material.bumpMap;\n\t\t\t\tuniforms.bumpScale.value = material.bumpScale;\n\n\t\t\t}\n\n\t\t\tif ( material.normalMap ) {\n\n\t\t\t\tuniforms.normalMap.value = material.normalMap;\n\t\t\t\tuniforms.normalScale.value.copy( material.normalScale );\n\n\t\t\t}\n\n\t\t\tif ( material.displacementMap ) {\n\n\t\t\t\tuniforms.displacementMap.value = material.displacementMap;\n\t\t\t\tuniforms.displacementScale.value = material.displacementScale;\n\t\t\t\tuniforms.displacementBias.value = material.displacementBias;\n\n\t\t\t}\n\n\t\t\tif ( material.envMap ) {\n\n\t\t\t\t//uniforms.envMap.value = material.envMap; // part of uniforms common\n\t\t\t\tuniforms.envMapIntensity.value = material.envMapIntensity;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction refreshUniformsPhysical( uniforms, material ) {\n\n\t\t\tuniforms.clearCoat.value = material.clearCoat;\n\t\t\tuniforms.clearCoatRoughness.value = material.clearCoatRoughness;\n\n\t\t\trefreshUniformsStandard( uniforms, material );\n\n\t\t}\n\n\t\t// If uniforms are marked as clean, they don't need to be loaded to the GPU.\n\n\t\tfunction markUniformsLightsNeedsUpdate( uniforms, value ) {\n\n\t\t\tuniforms.ambientLightColor.needsUpdate = value;\n\n\t\t\tuniforms.directionalLights.needsUpdate = value;\n\t\t\tuniforms.pointLights.needsUpdate = value;\n\t\t\tuniforms.spotLights.needsUpdate = value;\n\t\t\tuniforms.hemisphereLights.needsUpdate = value;\n\n\t\t}\n\n\t\t// Lighting\n\n\t\tfunction setupShadows( lights ) {\n\n\t\t\tvar lightShadowsLength = 0;\n\n\t\t\tfor ( var i = 0, l = lights.length; i < l; i ++ ) {\n\n\t\t\t\tvar light = lights[ i ];\n\n\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\t_lights.shadows[ lightShadowsLength ++ ] = light;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t_lights.shadows.length = lightShadowsLength;\n\n\t\t}\n\n\t\tfunction setupLights( lights, camera ) {\n\n\t\t\tvar l, ll, light,\n\t\t\tr = 0, g = 0, b = 0,\n\t\t\tcolor,\n\t\t\tintensity,\n\t\t\tdistance,\n\t\t\tshadowMap,\n\n\t\t\tviewMatrix = camera.matrixWorldInverse,\n\n\t\t\tdirectionalLength = 0,\n\t\t\tpointLength = 0,\n\t\t\tspotLength = 0,\n\t\t\themiLength = 0;\n\n\t\t\tfor ( l = 0, ll = lights.length; l < ll; l ++ ) {\n\n\t\t\t\tlight = lights[ l ];\n\n\t\t\t\tcolor = light.color;\n\t\t\t\tintensity = light.intensity;\n\t\t\t\tdistance = light.distance;\n\n\t\t\t\tshadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;\n\n\t\t\t\tif ( light.isAmbientLight ) {\n\n\t\t\t\t\tr += color.r * intensity;\n\t\t\t\t\tg += color.g * intensity;\n\t\t\t\t\tb += color.b * intensity;\n\n\t\t\t\t} else if ( light.isDirectionalLight ) {\n\n\t\t\t\t\tvar uniforms = lightCache.get( light );\n\n\t\t\t\t\tuniforms.color.copy( light.color ).multiplyScalar( light.intensity );\n\t\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\t\t_vector3.setFromMatrixPosition( light.target.matrixWorld );\n\t\t\t\t\tuniforms.direction.sub( _vector3 );\n\t\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\t\tuniforms.shadow = light.castShadow;\n\n\t\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\t\tuniforms.shadowBias = light.shadow.bias;\n\t\t\t\t\t\tuniforms.shadowRadius = light.shadow.radius;\n\t\t\t\t\t\tuniforms.shadowMapSize = light.shadow.mapSize;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_lights.directionalShadowMap[ directionalLength ] = shadowMap;\n\t\t\t\t\t_lights.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;\n\t\t\t\t\t_lights.directional[ directionalLength ++ ] = uniforms;\n\n\t\t\t\t} else if ( light.isSpotLight ) {\n\n\t\t\t\t\tvar uniforms = lightCache.get( light );\n\n\t\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\t\tuniforms.color.copy( color ).multiplyScalar( intensity );\n\t\t\t\t\tuniforms.distance = distance;\n\n\t\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\t\t_vector3.setFromMatrixPosition( light.target.matrixWorld );\n\t\t\t\t\tuniforms.direction.sub( _vector3 );\n\t\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\t\tuniforms.coneCos = Math.cos( light.angle );\n\t\t\t\t\tuniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );\n\t\t\t\t\tuniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;\n\n\t\t\t\t\tuniforms.shadow = light.castShadow;\n\n\t\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\t\tuniforms.shadowBias = light.shadow.bias;\n\t\t\t\t\t\tuniforms.shadowRadius = light.shadow.radius;\n\t\t\t\t\t\tuniforms.shadowMapSize = light.shadow.mapSize;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_lights.spotShadowMap[ spotLength ] = shadowMap;\n\t\t\t\t\t_lights.spotShadowMatrix[ spotLength ] = light.shadow.matrix;\n\t\t\t\t\t_lights.spot[ spotLength ++ ] = uniforms;\n\n\t\t\t\t} else if ( light.isPointLight ) {\n\n\t\t\t\t\tvar uniforms = lightCache.get( light );\n\n\t\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\t\tuniforms.color.copy( light.color ).multiplyScalar( light.intensity );\n\t\t\t\t\tuniforms.distance = light.distance;\n\t\t\t\t\tuniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;\n\n\t\t\t\t\tuniforms.shadow = light.castShadow;\n\n\t\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\t\tuniforms.shadowBias = light.shadow.bias;\n\t\t\t\t\t\tuniforms.shadowRadius = light.shadow.radius;\n\t\t\t\t\t\tuniforms.shadowMapSize = light.shadow.mapSize;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_lights.pointShadowMap[ pointLength ] = shadowMap;\n\n\t\t\t\t\tif ( _lights.pointShadowMatrix[ pointLength ] === undefined ) {\n\n\t\t\t\t\t\t_lights.pointShadowMatrix[ pointLength ] = new Matrix4();\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// for point lights we set the shadow matrix to be a translation-only matrix\n\t\t\t\t\t// equal to inverse of the light's position\n\t\t\t\t\t_vector3.setFromMatrixPosition( light.matrixWorld ).negate();\n\t\t\t\t\t_lights.pointShadowMatrix[ pointLength ].identity().setPosition( _vector3 );\n\n\t\t\t\t\t_lights.point[ pointLength ++ ] = uniforms;\n\n\t\t\t\t} else if ( light.isHemisphereLight ) {\n\n\t\t\t\t\tvar uniforms = lightCache.get( light );\n\n\t\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\t\t\t\t\tuniforms.direction.normalize();\n\n\t\t\t\t\tuniforms.skyColor.copy( light.color ).multiplyScalar( intensity );\n\t\t\t\t\tuniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity );\n\n\t\t\t\t\t_lights.hemi[ hemiLength ++ ] = uniforms;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t_lights.ambient[ 0 ] = r;\n\t\t\t_lights.ambient[ 1 ] = g;\n\t\t\t_lights.ambient[ 2 ] = b;\n\n\t\t\t_lights.directional.length = directionalLength;\n\t\t\t_lights.spot.length = spotLength;\n\t\t\t_lights.point.length = pointLength;\n\t\t\t_lights.hemi.length = hemiLength;\n\n\t\t\t_lights.hash = directionalLength + ',' + pointLength + ',' + spotLength + ',' + hemiLength + ',' + _lights.shadows.length;\n\n\t\t}\n\n\t\t// GL state setting\n\n\t\tthis.setFaceCulling = function ( cullFace, frontFaceDirection ) {\n\n\t\t\tstate.setCullFace( cullFace );\n\t\t\tstate.setFlipSided( frontFaceDirection === FrontFaceDirectionCW );\n\n\t\t};\n\n\t\t// Textures\n\n\t\tfunction allocTextureUnit() {\n\n\t\t\tvar textureUnit = _usedTextureUnits;\n\n\t\t\tif ( textureUnit >= capabilities.maxTextures ) {\n\n\t\t\t\tconsole.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );\n\n\t\t\t}\n\n\t\t\t_usedTextureUnits += 1;\n\n\t\t\treturn textureUnit;\n\n\t\t}\n\n\t\tthis.allocTextureUnit = allocTextureUnit;\n\n\t\t// this.setTexture2D = setTexture2D;\n\t\tthis.setTexture2D = ( function() {\n\n\t\t\tvar warned = false;\n\n\t\t\t// backwards compatibility: peel texture.texture\n\t\t\treturn function setTexture2D( texture, slot ) {\n\n\t\t\t\tif ( texture && texture.isWebGLRenderTarget ) {\n\n\t\t\t\t\tif ( ! warned ) {\n\n\t\t\t\t\t\tconsole.warn( \"THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead.\" );\n\t\t\t\t\t\twarned = true;\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture = texture.texture;\n\n\t\t\t\t}\n\n\t\t\t\ttextures.setTexture2D( texture, slot );\n\n\t\t\t};\n\n\t\t}() );\n\n\t\tthis.setTexture = ( function() {\n\n\t\t\tvar warned = false;\n\n\t\t\treturn function setTexture( texture, slot ) {\n\n\t\t\t\tif ( ! warned ) {\n\n\t\t\t\t\tconsole.warn( \"THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead.\" );\n\t\t\t\t\twarned = true;\n\n\t\t\t\t}\n\n\t\t\t\ttextures.setTexture2D( texture, slot );\n\n\t\t\t};\n\n\t\t}() );\n\n\t\tthis.setTextureCube = ( function() {\n\n\t\t\tvar warned = false;\n\n\t\t\treturn function setTextureCube( texture, slot ) {\n\n\t\t\t\t// backwards compatibility: peel texture.texture\n\t\t\t\tif ( texture && texture.isWebGLRenderTargetCube ) {\n\n\t\t\t\t\tif ( ! warned ) {\n\n\t\t\t\t\t\tconsole.warn( \"THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead.\" );\n\t\t\t\t\t\twarned = true;\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture = texture.texture;\n\n\t\t\t\t}\n\n\t\t\t\t// currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture\n\t\t\t\t// TODO: unify these code paths\n\t\t\t\tif ( ( texture && texture.isCubeTexture ) ||\n\t\t\t\t\t ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) {\n\n\t\t\t\t\t// CompressedTexture can have Array in image :/\n\n\t\t\t\t\t// this function alone should take care of cube textures\n\t\t\t\t\ttextures.setTextureCube( texture, slot );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// assumed: texture property of THREE.WebGLRenderTargetCube\n\n\t\t\t\t\ttextures.setTextureCubeDynamic( texture, slot );\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}() );\n\n\t\tthis.getCurrentRenderTarget = function() {\n\n\t\t\treturn _currentRenderTarget;\n\n\t\t};\n\n\t\tthis.setRenderTarget = function ( renderTarget ) {\n\n\t\t\t_currentRenderTarget = renderTarget;\n\n\t\t\tif ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {\n\n\t\t\t\ttextures.setupRenderTarget( renderTarget );\n\n\t\t\t}\n\n\t\t\tvar isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube );\n\t\t\tvar framebuffer;\n\n\t\t\tif ( renderTarget ) {\n\n\t\t\t\tvar renderTargetProperties = properties.get( renderTarget );\n\n\t\t\t\tif ( isCube ) {\n\n\t\t\t\t\tframebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ];\n\n\t\t\t\t} else {\n\n\t\t\t\t\tframebuffer = renderTargetProperties.__webglFramebuffer;\n\n\t\t\t\t}\n\n\t\t\t\t_currentScissor.copy( renderTarget.scissor );\n\t\t\t\t_currentScissorTest = renderTarget.scissorTest;\n\n\t\t\t\t_currentViewport.copy( renderTarget.viewport );\n\n\t\t\t} else {\n\n\t\t\t\tframebuffer = null;\n\n\t\t\t\t_currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio );\n\t\t\t\t_currentScissorTest = _scissorTest;\n\n\t\t\t\t_currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio );\n\n\t\t\t}\n\n\t\t\tif ( _currentFramebuffer !== framebuffer ) {\n\n\t\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\t\t\t\t_currentFramebuffer = framebuffer;\n\n\t\t\t}\n\n\t\t\tstate.scissor( _currentScissor );\n\t\t\tstate.setScissorTest( _currentScissorTest );\n\n\t\t\tstate.viewport( _currentViewport );\n\n\t\t\tif ( isCube ) {\n\n\t\t\t\tvar textureProperties = properties.get( renderTarget.texture );\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) {\n\n\t\t\tif ( ( renderTarget && renderTarget.isWebGLRenderTarget ) === false ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tvar framebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n\t\t\tif ( framebuffer ) {\n\n\t\t\t\tvar restore = false;\n\n\t\t\t\tif ( framebuffer !== _currentFramebuffer ) {\n\n\t\t\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\t\t\trestore = true;\n\n\t\t\t\t}\n\n\t\t\t\ttry {\n\n\t\t\t\t\tvar texture = renderTarget.texture;\n\t\t\t\t\tvar textureFormat = texture.format;\n\t\t\t\t\tvar textureType = texture.type;\n\n\t\t\t\t\tif ( textureFormat !== RGBAFormat && paramThreeToGL( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( textureType !== UnsignedByteType && paramThreeToGL( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513)\n\t\t\t\t\t ! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox\n\t\t\t\t\t ! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {\n\n\t\t\t\t\t\t// the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)\n\n\t\t\t\t\t\tif ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {\n\n\t\t\t\t\t\t\t_gl.readPixels( x, y, width, height, paramThreeToGL( textureFormat ), paramThreeToGL( textureType ), buffer );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t} finally {\n\n\t\t\t\t\tif ( restore ) {\n\n\t\t\t\t\t\t_gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\t// Map three.js constants to WebGL constants\n\n\t\tfunction paramThreeToGL( p ) {\n\n\t\t\tvar extension;\n\n\t\t\tif ( p === RepeatWrapping ) return _gl.REPEAT;\n\t\t\tif ( p === ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;\n\t\t\tif ( p === MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;\n\n\t\t\tif ( p === NearestFilter ) return _gl.NEAREST;\n\t\t\tif ( p === NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;\n\t\t\tif ( p === NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;\n\n\t\t\tif ( p === LinearFilter ) return _gl.LINEAR;\n\t\t\tif ( p === LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;\n\t\t\tif ( p === LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;\n\n\t\t\tif ( p === UnsignedByteType ) return _gl.UNSIGNED_BYTE;\n\t\t\tif ( p === UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;\n\t\t\tif ( p === UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;\n\t\t\tif ( p === UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;\n\n\t\t\tif ( p === ByteType ) return _gl.BYTE;\n\t\t\tif ( p === ShortType ) return _gl.SHORT;\n\t\t\tif ( p === UnsignedShortType ) return _gl.UNSIGNED_SHORT;\n\t\t\tif ( p === IntType ) return _gl.INT;\n\t\t\tif ( p === UnsignedIntType ) return _gl.UNSIGNED_INT;\n\t\t\tif ( p === FloatType ) return _gl.FLOAT;\n\n\t\t\tif ( p === HalfFloatType ) {\n\n\t\t\t\textension = extensions.get( 'OES_texture_half_float' );\n\n\t\t\t\tif ( extension !== null ) return extension.HALF_FLOAT_OES;\n\n\t\t\t}\n\n\t\t\tif ( p === AlphaFormat ) return _gl.ALPHA;\n\t\t\tif ( p === RGBFormat ) return _gl.RGB;\n\t\t\tif ( p === RGBAFormat ) return _gl.RGBA;\n\t\t\tif ( p === LuminanceFormat ) return _gl.LUMINANCE;\n\t\t\tif ( p === LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;\n\t\t\tif ( p === DepthFormat ) return _gl.DEPTH_COMPONENT;\n\t\t\tif ( p === DepthStencilFormat ) return _gl.DEPTH_STENCIL;\n\n\t\t\tif ( p === AddEquation ) return _gl.FUNC_ADD;\n\t\t\tif ( p === SubtractEquation ) return _gl.FUNC_SUBTRACT;\n\t\t\tif ( p === ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;\n\n\t\t\tif ( p === ZeroFactor ) return _gl.ZERO;\n\t\t\tif ( p === OneFactor ) return _gl.ONE;\n\t\t\tif ( p === SrcColorFactor ) return _gl.SRC_COLOR;\n\t\t\tif ( p === OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;\n\t\t\tif ( p === SrcAlphaFactor ) return _gl.SRC_ALPHA;\n\t\t\tif ( p === OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;\n\t\t\tif ( p === DstAlphaFactor ) return _gl.DST_ALPHA;\n\t\t\tif ( p === OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;\n\n\t\t\tif ( p === DstColorFactor ) return _gl.DST_COLOR;\n\t\t\tif ( p === OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;\n\t\t\tif ( p === SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;\n\n\t\t\tif ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format ||\n\t\t\t\tp === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {\n\n\t\t\t\textension = extensions.get( 'WEBGL_compressed_texture_s3tc' );\n\n\t\t\t\tif ( extension !== null ) {\n\n\t\t\t\t\tif ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format ||\n\t\t\t\t p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {\n\n\t\t\t\textension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );\n\n\t\t\t\tif ( extension !== null ) {\n\n\t\t\t\t\tif ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;\n\t\t\t\t\tif ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;\n\t\t\t\t\tif ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;\n\t\t\t\t\tif ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( p === RGB_ETC1_Format ) {\n\n\t\t\t\textension = extensions.get( 'WEBGL_compressed_texture_etc1' );\n\n\t\t\t\tif ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL;\n\n\t\t\t}\n\n\t\t\tif ( p === MinEquation || p === MaxEquation ) {\n\n\t\t\t\textension = extensions.get( 'EXT_blend_minmax' );\n\n\t\t\t\tif ( extension !== null ) {\n\n\t\t\t\t\tif ( p === MinEquation ) return extension.MIN_EXT;\n\t\t\t\t\tif ( p === MaxEquation ) return extension.MAX_EXT;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( p === UnsignedInt248Type ) {\n\n\t\t\t\textension = extensions.get( 'WEBGL_depth_texture' );\n\n\t\t\t\tif ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL;\n\n\t\t\t}\n\n\t\t\treturn 0;\n\n\t\t}\n\n\t}\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction FogExp2 ( color, density ) {\n\n\t\tthis.name = '';\n\n\t\tthis.color = new Color( color );\n\t\tthis.density = ( density !== undefined ) ? density : 0.00025;\n\n\t}\n\n\tFogExp2.prototype.isFogExp2 = true;\n\n\tFogExp2.prototype.clone = function () {\n\n\t\treturn new FogExp2( this.color.getHex(), this.density );\n\n\t};\n\n\tFogExp2.prototype.toJSON = function ( meta ) {\n\n\t\treturn {\n\t\t\ttype: 'FogExp2',\n\t\t\tcolor: this.color.getHex(),\n\t\t\tdensity: this.density\n\t\t};\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction Fog ( color, near, far ) {\n\n\t\tthis.name = '';\n\n\t\tthis.color = new Color( color );\n\n\t\tthis.near = ( near !== undefined ) ? near : 1;\n\t\tthis.far = ( far !== undefined ) ? far : 1000;\n\n\t}\n\n\tFog.prototype.isFog = true;\n\n\tFog.prototype.clone = function () {\n\n\t\treturn new Fog( this.color.getHex(), this.near, this.far );\n\n\t};\n\n\tFog.prototype.toJSON = function ( meta ) {\n\n\t\treturn {\n\t\t\ttype: 'Fog',\n\t\t\tcolor: this.color.getHex(),\n\t\t\tnear: this.near,\n\t\t\tfar: this.far\n\t\t};\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction Scene () {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Scene';\n\n\t\tthis.background = null;\n\t\tthis.fog = null;\n\t\tthis.overrideMaterial = null;\n\n\t\tthis.autoUpdate = true; // checked by the renderer\n\n\t}\n\n\tScene.prototype = Object.create( Object3D.prototype );\n\n\tScene.prototype.constructor = Scene;\n\n\tScene.prototype.copy = function ( source, recursive ) {\n\n\t\tObject3D.prototype.copy.call( this, source, recursive );\n\n\t\tif ( source.background !== null ) this.background = source.background.clone();\n\t\tif ( source.fog !== null ) this.fog = source.fog.clone();\n\t\tif ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();\n\n\t\tthis.autoUpdate = source.autoUpdate;\n\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\n\t\treturn this;\n\n\t};\n\n\tScene.prototype.toJSON = function ( meta ) {\n\n\t\tvar data = Object3D.prototype.toJSON.call( this, meta );\n\n\t\tif ( this.background !== null ) data.object.background = this.background.toJSON( meta );\n\t\tif ( this.fog !== null ) data.object.fog = this.fog.toJSON();\n\n\t\treturn data;\n\n\t};\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction LensFlare( texture, size, distance, blending, color ) {\n\n\t\tObject3D.call( this );\n\n\t\tthis.lensFlares = [];\n\n\t\tthis.positionScreen = new Vector3();\n\t\tthis.customUpdateCallback = undefined;\n\n\t\tif ( texture !== undefined ) {\n\n\t\t\tthis.add( texture, size, distance, blending, color );\n\n\t\t}\n\n\t}\n\n\tLensFlare.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: LensFlare,\n\n\t\tisLensFlare: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tObject3D.prototype.copy.call( this, source );\n\n\t\t\tthis.positionScreen.copy( source.positionScreen );\n\t\t\tthis.customUpdateCallback = source.customUpdateCallback;\n\n\t\t\tfor ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) {\n\n\t\t\t\tthis.lensFlares.push( source.lensFlares[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tadd: function ( texture, size, distance, blending, color, opacity ) {\n\n\t\t\tif ( size === undefined ) size = - 1;\n\t\t\tif ( distance === undefined ) distance = 0;\n\t\t\tif ( opacity === undefined ) opacity = 1;\n\t\t\tif ( color === undefined ) color = new Color( 0xffffff );\n\t\t\tif ( blending === undefined ) blending = NormalBlending;\n\n\t\t\tdistance = Math.min( distance, Math.max( 0, distance ) );\n\n\t\t\tthis.lensFlares.push( {\n\t\t\t\ttexture: texture,\t// THREE.Texture\n\t\t\t\tsize: size, \t\t// size in pixels (-1 = use texture.width)\n\t\t\t\tdistance: distance, \t// distance (0-1) from light source (0=at light source)\n\t\t\t\tx: 0, y: 0, z: 0,\t// screen position (-1 => 1) z = 0 is in front z = 1 is back\n\t\t\t\tscale: 1, \t\t// scale\n\t\t\t\trotation: 0, \t\t// rotation\n\t\t\t\topacity: opacity,\t// opacity\n\t\t\t\tcolor: color,\t\t// color\n\t\t\t\tblending: blending\t// blending\n\t\t\t} );\n\n\t\t},\n\n\t\t/*\n\t\t * Update lens flares update positions on all flares based on the screen position\n\t\t * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.\n\t\t */\n\n\t\tupdateLensFlares: function () {\n\n\t\t\tvar f, fl = this.lensFlares.length;\n\t\t\tvar flare;\n\t\t\tvar vecX = - this.positionScreen.x * 2;\n\t\t\tvar vecY = - this.positionScreen.y * 2;\n\n\t\t\tfor ( f = 0; f < fl; f ++ ) {\n\n\t\t\t\tflare = this.lensFlares[ f ];\n\n\t\t\t\tflare.x = this.positionScreen.x + vecX * flare.distance;\n\t\t\t\tflare.y = this.positionScreen.y + vecY * flare.distance;\n\n\t\t\t\tflare.wantedRotation = flare.x * Math.PI * 0.25;\n\t\t\t\tflare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;\n\n\t\t\t}\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * opacity: <float>,\n\t * map: new THREE.Texture( <Image> ),\n\t *\n\t *\tuvOffset: new THREE.Vector2(),\n\t *\tuvScale: new THREE.Vector2()\n\t * }\n\t */\n\n\tfunction SpriteMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'SpriteMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\t\tthis.map = null;\n\n\t\tthis.rotation = 0;\n\n\t\tthis.fog = false;\n\t\tthis.lights = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tSpriteMaterial.prototype = Object.create( Material.prototype );\n\tSpriteMaterial.prototype.constructor = SpriteMaterial;\n\n\tSpriteMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.color.copy( source.color );\n\t\tthis.map = source.map;\n\n\t\tthis.rotation = source.rotation;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction Sprite( material ) {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Sprite';\n\n\t\tthis.material = ( material !== undefined ) ? material : new SpriteMaterial();\n\n\t}\n\n\tSprite.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: Sprite,\n\n\t\tisSprite: true,\n\n\t\traycast: ( function () {\n\n\t\t\tvar matrixPosition = new Vector3();\n\n\t\t\treturn function raycast( raycaster, intersects ) {\n\n\t\t\t\tmatrixPosition.setFromMatrixPosition( this.matrixWorld );\n\n\t\t\t\tvar distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition );\n\t\t\t\tvar guessSizeSq = this.scale.x * this.scale.y / 4;\n\n\t\t\t\tif ( distanceSq > guessSizeSq ) {\n\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t\tintersects.push( {\n\n\t\t\t\t\tdistance: Math.sqrt( distanceSq ),\n\t\t\t\t\tpoint: this.position,\n\t\t\t\t\tface: null,\n\t\t\t\t\tobject: this\n\n\t\t\t\t} );\n\n\t\t\t};\n\n\t\t}() ),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.material ).copy( this );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction LOD() {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'LOD';\n\n\t\tObject.defineProperties( this, {\n\t\t\tlevels: {\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: []\n\t\t\t}\n\t\t} );\n\n\t}\n\n\n\tLOD.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: LOD,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tObject3D.prototype.copy.call( this, source, false );\n\n\t\t\tvar levels = source.levels;\n\n\t\t\tfor ( var i = 0, l = levels.length; i < l; i ++ ) {\n\n\t\t\t\tvar level = levels[ i ];\n\n\t\t\t\tthis.addLevel( level.object.clone(), level.distance );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\taddLevel: function ( object, distance ) {\n\n\t\t\tif ( distance === undefined ) distance = 0;\n\n\t\t\tdistance = Math.abs( distance );\n\n\t\t\tvar levels = this.levels;\n\n\t\t\tfor ( var l = 0; l < levels.length; l ++ ) {\n\n\t\t\t\tif ( distance < levels[ l ].distance ) {\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tlevels.splice( l, 0, { distance: distance, object: object } );\n\n\t\t\tthis.add( object );\n\n\t\t},\n\n\t\tgetObjectForDistance: function ( distance ) {\n\n\t\t\tvar levels = this.levels;\n\n\t\t\tfor ( var i = 1, l = levels.length; i < l; i ++ ) {\n\n\t\t\t\tif ( distance < levels[ i ].distance ) {\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn levels[ i - 1 ].object;\n\n\t\t},\n\n\t\traycast: ( function () {\n\n\t\t\tvar matrixPosition = new Vector3();\n\n\t\t\treturn function raycast( raycaster, intersects ) {\n\n\t\t\t\tmatrixPosition.setFromMatrixPosition( this.matrixWorld );\n\n\t\t\t\tvar distance = raycaster.ray.origin.distanceTo( matrixPosition );\n\n\t\t\t\tthis.getObjectForDistance( distance ).raycast( raycaster, intersects );\n\n\t\t\t};\n\n\t\t}() ),\n\n\t\tupdate: function () {\n\n\t\t\tvar v1 = new Vector3();\n\t\t\tvar v2 = new Vector3();\n\n\t\t\treturn function update( camera ) {\n\n\t\t\t\tvar levels = this.levels;\n\n\t\t\t\tif ( levels.length > 1 ) {\n\n\t\t\t\t\tv1.setFromMatrixPosition( camera.matrixWorld );\n\t\t\t\t\tv2.setFromMatrixPosition( this.matrixWorld );\n\n\t\t\t\t\tvar distance = v1.distanceTo( v2 );\n\n\t\t\t\t\tlevels[ 0 ].object.visible = true;\n\n\t\t\t\t\tfor ( var i = 1, l = levels.length; i < l; i ++ ) {\n\n\t\t\t\t\t\tif ( distance >= levels[ i ].distance ) {\n\n\t\t\t\t\t\t\tlevels[ i - 1 ].object.visible = false;\n\t\t\t\t\t\t\tlevels[ i ].object.visible = true;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( ; i < l; i ++ ) {\n\n\t\t\t\t\t\tlevels[ i ].object.visible = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}(),\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\tvar data = Object3D.prototype.toJSON.call( this, meta );\n\n\t\t\tdata.object.levels = [];\n\n\t\t\tvar levels = this.levels;\n\n\t\t\tfor ( var i = 0, l = levels.length; i < l; i ++ ) {\n\n\t\t\t\tvar level = levels[ i ];\n\n\t\t\t\tdata.object.levels.push( {\n\t\t\t\t\tobject: level.object.uuid,\n\t\t\t\t\tdistance: level.distance\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\treturn data;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {\n\n\t\tTexture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n\t\tthis.image = { data: data, width: width, height: height };\n\n\t\tthis.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n\t\tthis.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n\t\tthis.generateMipmaps = false;\n\t\tthis.flipY = false;\n\t\tthis.unpackAlignment = 1;\n\n\t}\n\n\tDataTexture.prototype = Object.create( Texture.prototype );\n\tDataTexture.prototype.constructor = DataTexture;\n\n\tDataTexture.prototype.isDataTexture = true;\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author michael guerrero / http://realitymeltdown.com\n\t * @author ikerr / http://verold.com\n\t */\n\n\tfunction Skeleton( bones, boneInverses, useVertexTexture ) {\n\n\t\tthis.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;\n\n\t\tthis.identityMatrix = new Matrix4();\n\n\t\t// copy the bone array\n\n\t\tbones = bones || [];\n\n\t\tthis.bones = bones.slice( 0 );\n\n\t\t// create a bone texture or an array of floats\n\n\t\tif ( this.useVertexTexture ) {\n\n\t\t\t// layout (1 matrix = 4 pixels)\n\t\t\t// RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)\n\t\t\t// with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8)\n\t\t\t// 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16)\n\t\t\t// 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32)\n\t\t\t// 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)\n\n\n\t\t\tvar size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix\n\t\t\tsize = _Math.nextPowerOfTwo( Math.ceil( size ) );\n\t\t\tsize = Math.max( size, 4 );\n\n\t\t\tthis.boneTextureWidth = size;\n\t\t\tthis.boneTextureHeight = size;\n\n\t\t\tthis.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel\n\t\t\tthis.boneTexture = new DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, RGBAFormat, FloatType );\n\n\t\t} else {\n\n\t\t\tthis.boneMatrices = new Float32Array( 16 * this.bones.length );\n\n\t\t}\n\n\t\t// use the supplied bone inverses or calculate the inverses\n\n\t\tif ( boneInverses === undefined ) {\n\n\t\t\tthis.calculateInverses();\n\n\t\t} else {\n\n\t\t\tif ( this.bones.length === boneInverses.length ) {\n\n\t\t\t\tthis.boneInverses = boneInverses.slice( 0 );\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'THREE.Skeleton bonInverses is the wrong length.' );\n\n\t\t\t\tthis.boneInverses = [];\n\n\t\t\t\tfor ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {\n\n\t\t\t\t\tthis.boneInverses.push( new Matrix4() );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tObject.assign( Skeleton.prototype, {\n\n\t\tcalculateInverses: function () {\n\n\t\t\tthis.boneInverses = [];\n\n\t\t\tfor ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {\n\n\t\t\t\tvar inverse = new Matrix4();\n\n\t\t\t\tif ( this.bones[ b ] ) {\n\n\t\t\t\t\tinverse.getInverse( this.bones[ b ].matrixWorld );\n\n\t\t\t\t}\n\n\t\t\t\tthis.boneInverses.push( inverse );\n\n\t\t\t}\n\n\t\t},\n\n\t\tpose: function () {\n\n\t\t\tvar bone;\n\n\t\t\t// recover the bind-time world matrices\n\n\t\t\tfor ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {\n\n\t\t\t\tbone = this.bones[ b ];\n\n\t\t\t\tif ( bone ) {\n\n\t\t\t\t\tbone.matrixWorld.getInverse( this.boneInverses[ b ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// compute the local matrices, positions, rotations and scales\n\n\t\t\tfor ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {\n\n\t\t\t\tbone = this.bones[ b ];\n\n\t\t\t\tif ( bone ) {\n\n\t\t\t\t\tif ( (bone.parent && bone.parent.isBone) ) {\n\n\t\t\t\t\t\tbone.matrix.getInverse( bone.parent.matrixWorld );\n\t\t\t\t\t\tbone.matrix.multiply( bone.matrixWorld );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tbone.matrix.copy( bone.matrixWorld );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbone.matrix.decompose( bone.position, bone.quaternion, bone.scale );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t},\n\n\t\tupdate: ( function () {\n\n\t\t\tvar offsetMatrix = new Matrix4();\n\n\t\t\treturn function update() {\n\n\t\t\t\t// flatten bone matrices to array\n\n\t\t\t\tfor ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {\n\n\t\t\t\t\t// compute the offset between the current and the original transform\n\n\t\t\t\t\tvar matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;\n\n\t\t\t\t\toffsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] );\n\t\t\t\t\toffsetMatrix.toArray( this.boneMatrices, b * 16 );\n\n\t\t\t\t}\n\n\t\t\t\tif ( this.useVertexTexture ) {\n\n\t\t\t\t\tthis.boneTexture.needsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t} )(),\n\n\t\tclone: function () {\n\n\t\t\treturn new Skeleton( this.bones, this.boneInverses, this.useVertexTexture );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author ikerr / http://verold.com\n\t */\n\n\tfunction Bone( skin ) {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Bone';\n\n\t\tthis.skin = skin;\n\n\t}\n\n\tBone.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: Bone,\n\n\t\tisBone: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tObject3D.prototype.copy.call( this, source );\n\n\t\t\tthis.skin = source.skin;\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mikael emtinger / http://gomo.se/\n\t * @author alteredq / http://alteredqualia.com/\n\t * @author ikerr / http://verold.com\n\t */\n\n\tfunction SkinnedMesh( geometry, material, useVertexTexture ) {\n\n\t\tMesh.call( this, geometry, material );\n\n\t\tthis.type = 'SkinnedMesh';\n\n\t\tthis.bindMode = \"attached\";\n\t\tthis.bindMatrix = new Matrix4();\n\t\tthis.bindMatrixInverse = new Matrix4();\n\n\t\t// init bones\n\n\t\t// TODO: remove bone creation as there is no reason (other than\n\t\t// convenience) for THREE.SkinnedMesh to do this.\n\n\t\tvar bones = [];\n\n\t\tif ( this.geometry && this.geometry.bones !== undefined ) {\n\n\t\t\tvar bone, gbone;\n\n\t\t\tfor ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {\n\n\t\t\t\tgbone = this.geometry.bones[ b ];\n\n\t\t\t\tbone = new Bone( this );\n\t\t\t\tbones.push( bone );\n\n\t\t\t\tbone.name = gbone.name;\n\t\t\t\tbone.position.fromArray( gbone.pos );\n\t\t\t\tbone.quaternion.fromArray( gbone.rotq );\n\t\t\t\tif ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );\n\n\t\t\t}\n\n\t\t\tfor ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {\n\n\t\t\t\tgbone = this.geometry.bones[ b ];\n\n\t\t\t\tif ( gbone.parent !== - 1 && gbone.parent !== null &&\n\t\t\t\t\t\tbones[ gbone.parent ] !== undefined ) {\n\n\t\t\t\t\tbones[ gbone.parent ].add( bones[ b ] );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis.add( bones[ b ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.normalizeSkinWeights();\n\n\t\tthis.updateMatrixWorld( true );\n\t\tthis.bind( new Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld );\n\n\t}\n\n\n\tSkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {\n\n\t\tconstructor: SkinnedMesh,\n\n\t\tisSkinnedMesh: true,\n\n\t\tbind: function( skeleton, bindMatrix ) {\n\n\t\t\tthis.skeleton = skeleton;\n\n\t\t\tif ( bindMatrix === undefined ) {\n\n\t\t\t\tthis.updateMatrixWorld( true );\n\n\t\t\t\tthis.skeleton.calculateInverses();\n\n\t\t\t\tbindMatrix = this.matrixWorld;\n\n\t\t\t}\n\n\t\t\tthis.bindMatrix.copy( bindMatrix );\n\t\t\tthis.bindMatrixInverse.getInverse( bindMatrix );\n\n\t\t},\n\n\t\tpose: function () {\n\n\t\t\tthis.skeleton.pose();\n\n\t\t},\n\n\t\tnormalizeSkinWeights: function () {\n\n\t\t\tif ( (this.geometry && this.geometry.isGeometry) ) {\n\n\t\t\t\tfor ( var i = 0; i < this.geometry.skinWeights.length; i ++ ) {\n\n\t\t\t\t\tvar sw = this.geometry.skinWeights[ i ];\n\n\t\t\t\t\tvar scale = 1.0 / sw.lengthManhattan();\n\n\t\t\t\t\tif ( scale !== Infinity ) {\n\n\t\t\t\t\t\tsw.multiplyScalar( scale );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tsw.set( 1, 0, 0, 0 ); // do something reasonable\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else if ( (this.geometry && this.geometry.isBufferGeometry) ) {\n\n\t\t\t\tvar vec = new Vector4();\n\n\t\t\t\tvar skinWeight = this.geometry.attributes.skinWeight;\n\n\t\t\t\tfor ( var i = 0; i < skinWeight.count; i ++ ) {\n\n\t\t\t\t\tvec.x = skinWeight.getX( i );\n\t\t\t\t\tvec.y = skinWeight.getY( i );\n\t\t\t\t\tvec.z = skinWeight.getZ( i );\n\t\t\t\t\tvec.w = skinWeight.getW( i );\n\n\t\t\t\t\tvar scale = 1.0 / vec.lengthManhattan();\n\n\t\t\t\t\tif ( scale !== Infinity ) {\n\n\t\t\t\t\t\tvec.multiplyScalar( scale );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tvec.set( 1, 0, 0, 0 ); // do something reasonable\n\n\t\t\t\t\t}\n\n\t\t\t\t\tskinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t},\n\n\t\tupdateMatrixWorld: function( force ) {\n\n\t\t\tMesh.prototype.updateMatrixWorld.call( this, true );\n\n\t\t\tif ( this.bindMode === \"attached\" ) {\n\n\t\t\t\tthis.bindMatrixInverse.getInverse( this.matrixWorld );\n\n\t\t\t} else if ( this.bindMode === \"detached\" ) {\n\n\t\t\t\tthis.bindMatrixInverse.getInverse( this.bindMatrix );\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode );\n\n\t\t\t}\n\n\t\t},\n\n\t\tclone: function() {\n\n\t\t\treturn new this.constructor( this.geometry, this.material, this.skeleton.useVertexTexture ).copy( this );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * opacity: <float>,\n\t *\n\t * linewidth: <float>,\n\t * linecap: \"round\",\n\t * linejoin: \"round\"\n\t * }\n\t */\n\n\tfunction LineBasicMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'LineBasicMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.linewidth = 1;\n\t\tthis.linecap = 'round';\n\t\tthis.linejoin = 'round';\n\n\t\tthis.lights = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tLineBasicMaterial.prototype = Object.create( Material.prototype );\n\tLineBasicMaterial.prototype.constructor = LineBasicMaterial;\n\n\tLineBasicMaterial.prototype.isLineBasicMaterial = true;\n\n\tLineBasicMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.linewidth = source.linewidth;\n\t\tthis.linecap = source.linecap;\n\t\tthis.linejoin = source.linejoin;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction Line( geometry, material, mode ) {\n\n\t\tif ( mode === 1 ) {\n\n\t\t\tconsole.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' );\n\t\t\treturn new LineSegments( geometry, material );\n\n\t\t}\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Line';\n\n\t\tthis.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n\t\tthis.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } );\n\n\t}\n\n\tLine.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: Line,\n\n\t\tisLine: true,\n\n\t\traycast: ( function () {\n\n\t\t\tvar inverseMatrix = new Matrix4();\n\t\t\tvar ray = new Ray();\n\t\t\tvar sphere = new Sphere();\n\n\t\t\treturn function raycast( raycaster, intersects ) {\n\n\t\t\t\tvar precision = raycaster.linePrecision;\n\t\t\t\tvar precisionSq = precision * precision;\n\n\t\t\t\tvar geometry = this.geometry;\n\t\t\t\tvar matrixWorld = this.matrixWorld;\n\n\t\t\t\t// Checking boundingSphere distance to ray\n\n\t\t\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t\t\tsphere.copy( geometry.boundingSphere );\n\t\t\t\tsphere.applyMatrix4( matrixWorld );\n\n\t\t\t\tif ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n\t\t\t\t//\n\n\t\t\t\tinverseMatrix.getInverse( matrixWorld );\n\t\t\t\tray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n\t\t\t\tvar vStart = new Vector3();\n\t\t\t\tvar vEnd = new Vector3();\n\t\t\t\tvar interSegment = new Vector3();\n\t\t\t\tvar interRay = new Vector3();\n\t\t\t\tvar step = (this && this.isLineSegments) ? 2 : 1;\n\n\t\t\t\tif ( (geometry && geometry.isBufferGeometry) ) {\n\n\t\t\t\t\tvar index = geometry.index;\n\t\t\t\t\tvar attributes = geometry.attributes;\n\t\t\t\t\tvar positions = attributes.position.array;\n\n\t\t\t\t\tif ( index !== null ) {\n\n\t\t\t\t\t\tvar indices = index.array;\n\n\t\t\t\t\t\tfor ( var i = 0, l = indices.length - 1; i < l; i += step ) {\n\n\t\t\t\t\t\t\tvar a = indices[ i ];\n\t\t\t\t\t\t\tvar b = indices[ i + 1 ];\n\n\t\t\t\t\t\t\tvStart.fromArray( positions, a * 3 );\n\t\t\t\t\t\t\tvEnd.fromArray( positions, b * 3 );\n\n\t\t\t\t\t\t\tvar distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n\t\t\t\t\t\t\tif ( distSq > precisionSq ) continue;\n\n\t\t\t\t\t\t\tinterRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n\t\t\t\t\t\t\tvar distance = raycaster.ray.origin.distanceTo( interRay );\n\n\t\t\t\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n\t\t\t\t\t\t\tintersects.push( {\n\n\t\t\t\t\t\t\t\tdistance: distance,\n\t\t\t\t\t\t\t\t// What do we want? intersection point on the ray or on the segment??\n\t\t\t\t\t\t\t\t// point: raycaster.ray.at( distance ),\n\t\t\t\t\t\t\t\tpoint: interSegment.clone().applyMatrix4( this.matrixWorld ),\n\t\t\t\t\t\t\t\tindex: i,\n\t\t\t\t\t\t\t\tface: null,\n\t\t\t\t\t\t\t\tfaceIndex: null,\n\t\t\t\t\t\t\t\tobject: this\n\n\t\t\t\t\t\t\t} );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tfor ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) {\n\n\t\t\t\t\t\t\tvStart.fromArray( positions, 3 * i );\n\t\t\t\t\t\t\tvEnd.fromArray( positions, 3 * i + 3 );\n\n\t\t\t\t\t\t\tvar distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n\t\t\t\t\t\t\tif ( distSq > precisionSq ) continue;\n\n\t\t\t\t\t\t\tinterRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n\t\t\t\t\t\t\tvar distance = raycaster.ray.origin.distanceTo( interRay );\n\n\t\t\t\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n\t\t\t\t\t\t\tintersects.push( {\n\n\t\t\t\t\t\t\t\tdistance: distance,\n\t\t\t\t\t\t\t\t// What do we want? intersection point on the ray or on the segment??\n\t\t\t\t\t\t\t\t// point: raycaster.ray.at( distance ),\n\t\t\t\t\t\t\t\tpoint: interSegment.clone().applyMatrix4( this.matrixWorld ),\n\t\t\t\t\t\t\t\tindex: i,\n\t\t\t\t\t\t\t\tface: null,\n\t\t\t\t\t\t\t\tfaceIndex: null,\n\t\t\t\t\t\t\t\tobject: this\n\n\t\t\t\t\t\t\t} );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( (geometry && geometry.isGeometry) ) {\n\n\t\t\t\t\tvar vertices = geometry.vertices;\n\t\t\t\t\tvar nbVertices = vertices.length;\n\n\t\t\t\t\tfor ( var i = 0; i < nbVertices - 1; i += step ) {\n\n\t\t\t\t\t\tvar distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );\n\n\t\t\t\t\t\tif ( distSq > precisionSq ) continue;\n\n\t\t\t\t\t\tinterRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n\t\t\t\t\t\tvar distance = raycaster.ray.origin.distanceTo( interRay );\n\n\t\t\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n\t\t\t\t\t\tintersects.push( {\n\n\t\t\t\t\t\t\tdistance: distance,\n\t\t\t\t\t\t\t// What do we want? intersection point on the ray or on the segment??\n\t\t\t\t\t\t\t// point: raycaster.ray.at( distance ),\n\t\t\t\t\t\t\tpoint: interSegment.clone().applyMatrix4( this.matrixWorld ),\n\t\t\t\t\t\t\tindex: i,\n\t\t\t\t\t\t\tface: null,\n\t\t\t\t\t\t\tfaceIndex: null,\n\t\t\t\t\t\t\tobject: this\n\n\t\t\t\t\t\t} );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}() ),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.geometry, this.material ).copy( this );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction LineSegments( geometry, material ) {\n\n\t\tLine.call( this, geometry, material );\n\n\t\tthis.type = 'LineSegments';\n\n\t}\n\n\tLineSegments.prototype = Object.assign( Object.create( Line.prototype ), {\n\n\t\tconstructor: LineSegments,\n\n\t\tisLineSegments: true\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * opacity: <float>,\n\t * map: new THREE.Texture( <Image> ),\n\t *\n\t * size: <float>,\n\t * sizeAttenuation: <bool>\n\t * }\n\t */\n\n\tfunction PointsMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'PointsMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.map = null;\n\n\t\tthis.size = 1;\n\t\tthis.sizeAttenuation = true;\n\n\t\tthis.lights = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tPointsMaterial.prototype = Object.create( Material.prototype );\n\tPointsMaterial.prototype.constructor = PointsMaterial;\n\n\tPointsMaterial.prototype.isPointsMaterial = true;\n\n\tPointsMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.size = source.size;\n\t\tthis.sizeAttenuation = source.sizeAttenuation;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction Points( geometry, material ) {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Points';\n\n\t\tthis.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n\t\tthis.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } );\n\n\t}\n\n\tPoints.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: Points,\n\n\t\tisPoints: true,\n\n\t\traycast: ( function () {\n\n\t\t\tvar inverseMatrix = new Matrix4();\n\t\t\tvar ray = new Ray();\n\t\t\tvar sphere = new Sphere();\n\n\t\t\treturn function raycast( raycaster, intersects ) {\n\n\t\t\t\tvar object = this;\n\t\t\t\tvar geometry = this.geometry;\n\t\t\t\tvar matrixWorld = this.matrixWorld;\n\t\t\t\tvar threshold = raycaster.params.Points.threshold;\n\n\t\t\t\t// Checking boundingSphere distance to ray\n\n\t\t\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t\t\tsphere.copy( geometry.boundingSphere );\n\t\t\t\tsphere.applyMatrix4( matrixWorld );\n\n\t\t\t\tif ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n\t\t\t\t//\n\n\t\t\t\tinverseMatrix.getInverse( matrixWorld );\n\t\t\t\tray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n\t\t\t\tvar localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );\n\t\t\t\tvar localThresholdSq = localThreshold * localThreshold;\n\t\t\t\tvar position = new Vector3();\n\n\t\t\t\tfunction testPoint( point, index ) {\n\n\t\t\t\t\tvar rayPointDistanceSq = ray.distanceSqToPoint( point );\n\n\t\t\t\t\tif ( rayPointDistanceSq < localThresholdSq ) {\n\n\t\t\t\t\t\tvar intersectPoint = ray.closestPointToPoint( point );\n\t\t\t\t\t\tintersectPoint.applyMatrix4( matrixWorld );\n\n\t\t\t\t\t\tvar distance = raycaster.ray.origin.distanceTo( intersectPoint );\n\n\t\t\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) return;\n\n\t\t\t\t\t\tintersects.push( {\n\n\t\t\t\t\t\t\tdistance: distance,\n\t\t\t\t\t\t\tdistanceToRay: Math.sqrt( rayPointDistanceSq ),\n\t\t\t\t\t\t\tpoint: intersectPoint.clone(),\n\t\t\t\t\t\t\tindex: index,\n\t\t\t\t\t\t\tface: null,\n\t\t\t\t\t\t\tobject: object\n\n\t\t\t\t\t\t} );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( (geometry && geometry.isBufferGeometry) ) {\n\n\t\t\t\t\tvar index = geometry.index;\n\t\t\t\t\tvar attributes = geometry.attributes;\n\t\t\t\t\tvar positions = attributes.position.array;\n\n\t\t\t\t\tif ( index !== null ) {\n\n\t\t\t\t\t\tvar indices = index.array;\n\n\t\t\t\t\t\tfor ( var i = 0, il = indices.length; i < il; i ++ ) {\n\n\t\t\t\t\t\t\tvar a = indices[ i ];\n\n\t\t\t\t\t\t\tposition.fromArray( positions, a * 3 );\n\n\t\t\t\t\t\t\ttestPoint( position, a );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tfor ( var i = 0, l = positions.length / 3; i < l; i ++ ) {\n\n\t\t\t\t\t\t\tposition.fromArray( positions, i * 3 );\n\n\t\t\t\t\t\t\ttestPoint( position, i );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tvar vertices = geometry.vertices;\n\n\t\t\t\t\tfor ( var i = 0, l = vertices.length; i < l; i ++ ) {\n\n\t\t\t\t\t\ttestPoint( vertices[ i ], i );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}() ),\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor( this.geometry, this.material ).copy( this );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction Group() {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Group';\n\n\t}\n\n\tGroup.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: Group\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n\t\tTexture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.generateMipmaps = false;\n\n\t\tvar scope = this;\n\n\t\tfunction update() {\n\n\t\t\trequestAnimationFrame( update );\n\n\t\t\tif ( video.readyState >= video.HAVE_CURRENT_DATA ) {\n\n\t\t\t\tscope.needsUpdate = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tupdate();\n\n\t}\n\n\tVideoTexture.prototype = Object.create( Texture.prototype );\n\tVideoTexture.prototype.constructor = VideoTexture;\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {\n\n\t\tTexture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n\t\tthis.image = { width: width, height: height };\n\t\tthis.mipmaps = mipmaps;\n\n\t\t// no flipping for cube textures\n\t\t// (also flipping doesn't work for compressed textures )\n\n\t\tthis.flipY = false;\n\n\t\t// can't generate mipmaps for compressed textures\n\t\t// mips must be embedded in DDS files\n\n\t\tthis.generateMipmaps = false;\n\n\t}\n\n\tCompressedTexture.prototype = Object.create( Texture.prototype );\n\tCompressedTexture.prototype.constructor = CompressedTexture;\n\n\tCompressedTexture.prototype.isCompressedTexture = true;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n\t\tTexture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.needsUpdate = true;\n\n\t}\n\n\tCanvasTexture.prototype = Object.create( Texture.prototype );\n\tCanvasTexture.prototype.constructor = CanvasTexture;\n\n\t/**\n\t * @author Matt DesLauriers / @mattdesl\n\t * @author atix / arthursilber.de\n\t */\n\n\tfunction DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) {\n\n\t\tformat = format !== undefined ? format : DepthFormat;\n\n\t\tif ( format !== DepthFormat && format !== DepthStencilFormat ) {\n\n\t\t\tthrow new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' )\n\n\t\t}\n\n\t\tTexture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.image = { width: width, height: height };\n\n\t\tthis.type = type !== undefined ? type : UnsignedShortType;\n\n\t\tthis.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n\t\tthis.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n\t\tthis.flipY = false;\n\t\tthis.generateMipmaps\t= false;\n\n\t}\n\n\tDepthTexture.prototype = Object.create( Texture.prototype );\n\tDepthTexture.prototype.constructor = DepthTexture;\n\tDepthTexture.prototype.isDepthTexture = true;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction WireframeGeometry( geometry ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tvar edge = [ 0, 0 ], hash = {};\n\n\t\tfunction sortFunction( a, b ) {\n\n\t\t\treturn a - b;\n\n\t\t}\n\n\t\tvar keys = [ 'a', 'b', 'c' ];\n\n\t\tif ( (geometry && geometry.isGeometry) ) {\n\n\t\t\tvar vertices = geometry.vertices;\n\t\t\tvar faces = geometry.faces;\n\t\t\tvar numEdges = 0;\n\n\t\t\t// allocate maximal size\n\t\t\tvar edges = new Uint32Array( 6 * faces.length );\n\n\t\t\tfor ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n\t\t\t\tvar face = faces[ i ];\n\n\t\t\t\tfor ( var j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\tedge[ 0 ] = face[ keys[ j ] ];\n\t\t\t\t\tedge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];\n\t\t\t\t\tedge.sort( sortFunction );\n\n\t\t\t\t\tvar key = edge.toString();\n\n\t\t\t\t\tif ( hash[ key ] === undefined ) {\n\n\t\t\t\t\t\tedges[ 2 * numEdges ] = edge[ 0 ];\n\t\t\t\t\t\tedges[ 2 * numEdges + 1 ] = edge[ 1 ];\n\t\t\t\t\t\thash[ key ] = true;\n\t\t\t\t\t\tnumEdges ++;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar coords = new Float32Array( numEdges * 2 * 3 );\n\n\t\t\tfor ( var i = 0, l = numEdges; i < l; i ++ ) {\n\n\t\t\t\tfor ( var j = 0; j < 2; j ++ ) {\n\n\t\t\t\t\tvar vertex = vertices[ edges [ 2 * i + j ] ];\n\n\t\t\t\t\tvar index = 6 * i + 3 * j;\n\t\t\t\t\tcoords[ index + 0 ] = vertex.x;\n\t\t\t\t\tcoords[ index + 1 ] = vertex.y;\n\t\t\t\t\tcoords[ index + 2 ] = vertex.z;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.addAttribute( 'position', new BufferAttribute( coords, 3 ) );\n\n\t\t} else if ( (geometry && geometry.isBufferGeometry) ) {\n\n\t\t\tif ( geometry.index !== null ) {\n\n\t\t\t\t// Indexed BufferGeometry\n\n\t\t\t\tvar indices = geometry.index.array;\n\t\t\t\tvar vertices = geometry.attributes.position;\n\t\t\t\tvar groups = geometry.groups;\n\t\t\t\tvar numEdges = 0;\n\n\t\t\t\tif ( groups.length === 0 ) {\n\n\t\t\t\t\tgeometry.addGroup( 0, indices.length );\n\n\t\t\t\t}\n\n\t\t\t\t// allocate maximal size\n\t\t\t\tvar edges = new Uint32Array( 2 * indices.length );\n\n\t\t\t\tfor ( var o = 0, ol = groups.length; o < ol; ++ o ) {\n\n\t\t\t\t\tvar group = groups[ o ];\n\n\t\t\t\t\tvar start = group.start;\n\t\t\t\t\tvar count = group.count;\n\n\t\t\t\t\tfor ( var i = start, il = start + count; i < il; i += 3 ) {\n\n\t\t\t\t\t\tfor ( var j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\t\t\tedge[ 0 ] = indices[ i + j ];\n\t\t\t\t\t\t\tedge[ 1 ] = indices[ i + ( j + 1 ) % 3 ];\n\t\t\t\t\t\t\tedge.sort( sortFunction );\n\n\t\t\t\t\t\t\tvar key = edge.toString();\n\n\t\t\t\t\t\t\tif ( hash[ key ] === undefined ) {\n\n\t\t\t\t\t\t\t\tedges[ 2 * numEdges ] = edge[ 0 ];\n\t\t\t\t\t\t\t\tedges[ 2 * numEdges + 1 ] = edge[ 1 ];\n\t\t\t\t\t\t\t\thash[ key ] = true;\n\t\t\t\t\t\t\t\tnumEdges ++;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tvar coords = new Float32Array( numEdges * 2 * 3 );\n\n\t\t\t\tfor ( var i = 0, l = numEdges; i < l; i ++ ) {\n\n\t\t\t\t\tfor ( var j = 0; j < 2; j ++ ) {\n\n\t\t\t\t\t\tvar index = 6 * i + 3 * j;\n\t\t\t\t\t\tvar index2 = edges[ 2 * i + j ];\n\n\t\t\t\t\t\tcoords[ index + 0 ] = vertices.getX( index2 );\n\t\t\t\t\t\tcoords[ index + 1 ] = vertices.getY( index2 );\n\t\t\t\t\t\tcoords[ index + 2 ] = vertices.getZ( index2 );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tthis.addAttribute( 'position', new BufferAttribute( coords, 3 ) );\n\n\t\t\t} else {\n\n\t\t\t\t// non-indexed BufferGeometry\n\n\t\t\t\tvar vertices = geometry.attributes.position.array;\n\t\t\t\tvar numEdges = vertices.length / 3;\n\t\t\t\tvar numTris = numEdges / 3;\n\n\t\t\t\tvar coords = new Float32Array( numEdges * 2 * 3 );\n\n\t\t\t\tfor ( var i = 0, l = numTris; i < l; i ++ ) {\n\n\t\t\t\t\tfor ( var j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\t\tvar index = 18 * i + 6 * j;\n\n\t\t\t\t\t\tvar index1 = 9 * i + 3 * j;\n\t\t\t\t\t\tcoords[ index + 0 ] = vertices[ index1 ];\n\t\t\t\t\t\tcoords[ index + 1 ] = vertices[ index1 + 1 ];\n\t\t\t\t\t\tcoords[ index + 2 ] = vertices[ index1 + 2 ];\n\n\t\t\t\t\t\tvar index2 = 9 * i + 3 * ( ( j + 1 ) % 3 );\n\t\t\t\t\t\tcoords[ index + 3 ] = vertices[ index2 ];\n\t\t\t\t\t\tcoords[ index + 4 ] = vertices[ index2 + 1 ];\n\t\t\t\t\t\tcoords[ index + 5 ] = vertices[ index2 + 2 ];\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tthis.addAttribute( 'position', new BufferAttribute( coords, 3 ) );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tWireframeGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tWireframeGeometry.prototype.constructor = WireframeGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t *\n\t * Parametric Surfaces Geometry\n\t * based on the brilliant article by @prideout http://prideout.net/blog/?p=44\n\t */\n\n\tfunction ParametricBufferGeometry( func, slices, stacks ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'ParametricBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tfunc: func,\n\t\t\tslices: slices,\n\t\t\tstacks: stacks\n\t\t};\n\n\t\t// generate vertices and uvs\n\n\t\tvar vertices = [];\n\t\tvar uvs = [];\n\n\t\tvar i, j, p;\n\t\tvar u, v;\n\n\t\tvar sliceCount = slices + 1;\n\n\t\tfor ( i = 0; i <= stacks; i ++ ) {\n\n\t\t\tv = i / stacks;\n\n\t\t\tfor ( j = 0; j <= slices; j ++ ) {\n\n\t\t\t\tu = j / slices;\n\n\t\t\t\tp = func( u, v );\n\t\t\t\tvertices.push( p.x, p.y, p.z );\n\n\t\t\t\tuvs.push( u, v );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tvar indices = [];\n\t\tvar a, b, c, d;\n\n\t\tfor ( i = 0; i < stacks; i ++ ) {\n\n\t\t\tfor ( j = 0; j < slices; j ++ ) {\n\n\t\t\t\ta = i * sliceCount + j;\n\t\t\t\tb = i * sliceCount + j + 1;\n\t\t\t\tc = ( i + 1 ) * sliceCount + j + 1;\n\t\t\t\td = ( i + 1 ) * sliceCount + j;\n\n\t\t\t\t// faces one and two\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( ( indices.length > 65535 ? Uint32Attribute : Uint16Attribute )( indices, 1 ) );\n\t\tthis.addAttribute( 'position', Float32Attribute( vertices, 3 ) );\n\t\tthis.addAttribute( 'uv', Float32Attribute( uvs, 2 ) );\n\n\t\t// generate normals\n\n\t\tthis.computeVertexNormals();\n\n\t}\n\n\tParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry;\n\n\t/**\n\t * @author zz85 / https://github.com/zz85\n\t *\n\t * Parametric Surfaces Geometry\n\t * based on the brilliant article by @prideout http://prideout.net/blog/?p=44\n\t */\n\n\tfunction ParametricGeometry( func, slices, stacks ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'ParametricGeometry';\n\n\t\tthis.parameters = {\n\t\t\tfunc: func,\n\t\t\tslices: slices,\n\t\t\tstacks: stacks\n\t\t};\n\n\t\tthis.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tParametricGeometry.prototype = Object.create( Geometry.prototype );\n\tParametricGeometry.prototype.constructor = ParametricGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction PolyhedronBufferGeometry( vertices, indices, radius, detail ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'PolyhedronBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tvertices: vertices,\n\t\t\tindices: indices,\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\tradius = radius || 1;\n\t\tdetail = detail || 0;\n\n\t\t// default buffer data\n\n\t\tvar vertexBuffer = [];\n\t\tvar uvBuffer = [];\n\n\t\t// the subdivision creates the vertex buffer data\n\n\t\tsubdivide( detail );\n\n\t\t// all vertices should lie on a conceptual sphere with a given radius\n\n\t\tappplyRadius( radius );\n\n\t\t// finally, create the uv data\n\n\t\tgenerateUVs();\n\n\t\t// build non-indexed geometry\n\n\t\tthis.addAttribute( 'position', Float32Attribute( vertexBuffer, 3 ) );\n\t\tthis.addAttribute( 'normal', Float32Attribute( vertexBuffer.slice(), 3 ) );\n\t\tthis.addAttribute( 'uv', Float32Attribute( uvBuffer, 2 ) );\n\t\tthis.normalizeNormals();\n\n\t\tthis.boundingSphere = new Sphere( new Vector3(), radius );\n\n\t\t// helper functions\n\n\t\tfunction subdivide( detail ) {\n\n\t\t\tvar a = new Vector3();\n\t\t\tvar b = new Vector3();\n\t\t\tvar c = new Vector3();\n\n\t\t\t// iterate over all faces and apply a subdivison with the given detail value\n\n\t\t\tfor ( var i = 0; i < indices.length; i += 3 ) {\n\n\t\t\t\t// get the vertices of the face\n\n\t\t\t\tgetVertexByIndex( indices[ i + 0 ], a );\n\t\t\t\tgetVertexByIndex( indices[ i + 1 ], b );\n\t\t\t\tgetVertexByIndex( indices[ i + 2 ], c );\n\n\t\t\t\t// perform subdivision\n\n\t\t\t\tsubdivideFace( a, b, c, detail );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction subdivideFace( a, b, c, detail ) {\n\n\t\t\tvar cols = Math.pow( 2, detail );\n\n\t\t\t// we use this multidimensional array as a data structure for creating the subdivision\n\n\t\t\tvar v = [];\n\n\t\t\tvar i, j;\n\n\t\t\t// construct all of the vertices for this subdivision\n\n\t\t\tfor ( i = 0 ; i <= cols; i ++ ) {\n\n\t\t\t\tv[ i ] = [];\n\n\t\t\t\tvar aj = a.clone().lerp( c, i / cols );\n\t\t\t\tvar bj = b.clone().lerp( c, i / cols );\n\n\t\t\t\tvar rows = cols - i;\n\n\t\t\t\tfor ( j = 0; j <= rows; j ++ ) {\n\n\t\t\t\t\tif ( j === 0 && i === cols ) {\n\n\t\t\t\t\t\tv[ i ][ j ] = aj;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tv[ i ][ j ] = aj.clone().lerp( bj, j / rows );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// construct all of the faces\n\n\t\t\tfor ( i = 0; i < cols ; i ++ ) {\n\n\t\t\t\tfor ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {\n\n\t\t\t\t\tvar k = Math.floor( j / 2 );\n\n\t\t\t\t\tif ( j % 2 === 0 ) {\n\n\t\t\t\t\t\tpushVertex( v[ i ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k ] );\n\t\t\t\t\t\tpushVertex( v[ i ][ k ] );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tpushVertex( v[ i ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction appplyRadius( radius ) {\n\n\t\t\tvar vertex = new Vector3();\n\n\t\t\t// iterate over the entire buffer and apply the radius to each vertex\n\n\t\t\tfor ( var i = 0; i < vertexBuffer.length; i += 3 ) {\n\n\t\t\t\tvertex.x = vertexBuffer[ i + 0 ];\n\t\t\t\tvertex.y = vertexBuffer[ i + 1 ];\n\t\t\t\tvertex.z = vertexBuffer[ i + 2 ];\n\n\t\t\t\tvertex.normalize().multiplyScalar( radius );\n\n\t\t\t\tvertexBuffer[ i + 0 ] = vertex.x;\n\t\t\t\tvertexBuffer[ i + 1 ] = vertex.y;\n\t\t\t\tvertexBuffer[ i + 2 ] = vertex.z;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction generateUVs() {\n\n\t\t\tvar vertex = new Vector3();\n\n\t\t\tfor ( var i = 0; i < vertexBuffer.length; i += 3 ) {\n\n\t\t\t\tvertex.x = vertexBuffer[ i + 0 ];\n\t\t\t\tvertex.y = vertexBuffer[ i + 1 ];\n\t\t\t\tvertex.z = vertexBuffer[ i + 2 ];\n\n\t\t\t\tvar u = azimuth( vertex ) / 2 / Math.PI + 0.5;\n\t\t\t\tvar v = inclination( vertex ) / Math.PI + 0.5;\n\t\t\t\tuvBuffer.push( u, 1 - v );\n\n\t\t\t}\n\n\t\t\tcorrectUVs();\n\n\t\t\tcorrectSeam();\n\n\t\t}\n\n\t\tfunction correctSeam() {\n\n\t\t\t// handle case when face straddles the seam, see #3269\n\n\t\t\tfor ( var i = 0; i < uvBuffer.length; i += 6 ) {\n\n\t\t\t\t// uv data of a single face\n\n\t\t\t\tvar x0 = uvBuffer[ i + 0 ];\n\t\t\t\tvar x1 = uvBuffer[ i + 2 ];\n\t\t\t\tvar x2 = uvBuffer[ i + 4 ];\n\n\t\t\t\tvar max = Math.max( x0, x1, x2 );\n\t\t\t\tvar min = Math.min( x0, x1, x2 );\n\n\t\t\t\t// 0.9 is somewhat arbitrary\n\n\t\t\t\tif ( max > 0.9 && min < 0.1 ) {\n\n\t\t\t\t\tif ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;\n\t\t\t\t\tif ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;\n\t\t\t\t\tif ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction pushVertex( vertex ) {\n\n\t\t\tvertexBuffer.push( vertex.x, vertex.y, vertex.z );\n\n\t\t}\n\n\t\tfunction getVertexByIndex( index, vertex ) {\n\n\t\t\tvar stride = index * 3;\n\n\t\t\tvertex.x = vertices[ stride + 0 ];\n\t\t\tvertex.y = vertices[ stride + 1 ];\n\t\t\tvertex.z = vertices[ stride + 2 ];\n\n\t\t}\n\n\t\tfunction correctUVs() {\n\n\t\t\tvar a = new Vector3();\n\t\t\tvar b = new Vector3();\n\t\t\tvar c = new Vector3();\n\n\t\t\tvar centroid = new Vector3();\n\n\t\t\tvar uvA = new Vector2();\n\t\t\tvar uvB = new Vector2();\n\t\t\tvar uvC = new Vector2();\n\n\t\t\tfor ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {\n\n\t\t\t\ta.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );\n\t\t\t\tb.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );\n\t\t\t\tc.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );\n\n\t\t\t\tuvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );\n\t\t\t\tuvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );\n\t\t\t\tuvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );\n\n\t\t\t\tcentroid.copy( a ).add( b ).add( c ).divideScalar( 3 );\n\n\t\t\t\tvar azi = azimuth( centroid );\n\n\t\t\t\tcorrectUV( uvA, j + 0, a, azi );\n\t\t\t\tcorrectUV( uvB, j + 2, b, azi );\n\t\t\t\tcorrectUV( uvC, j + 4, c, azi );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction correctUV( uv, stride, vector, azimuth ) {\n\n\t\t\tif ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {\n\n\t\t\t\tuvBuffer[ stride ] = uv.x - 1;\n\n\t\t\t}\n\n\t\t\tif ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {\n\n\t\t\t\tuvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Angle around the Y axis, counter-clockwise when looking from above.\n\n\t\tfunction azimuth( vector ) {\n\n\t\t\treturn Math.atan2( vector.z, - vector.x );\n\n\t\t}\n\n\n\t\t// Angle above the XZ plane.\n\n\t\tfunction inclination( vector ) {\n\n\t\t\treturn Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );\n\n\t\t}\n\n\t}\n\n\tPolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tPolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction TetrahedronBufferGeometry( radius, detail ) {\n\n\t\tvar vertices = [\n\t\t\t1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1\n\t\t];\n\n\t\tvar indices = [\n\t\t\t2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1\n\t\t];\n\n\t\tPolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n\t\tthis.type = 'TetrahedronBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tTetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n\tTetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry;\n\n\t/**\n\t * @author timothypratley / https://github.com/timothypratley\n\t */\n\n\tfunction TetrahedronGeometry( radius, detail ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'TetrahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\tthis.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tTetrahedronGeometry.prototype = Object.create( Geometry.prototype );\n\tTetrahedronGeometry.prototype.constructor = TetrahedronGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction OctahedronBufferGeometry( radius,detail ) {\n\n\t\tvar vertices = [\n\t\t\t1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1\n\t\t];\n\n\t\tvar indices = [\n\t\t\t0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2\n\t\t];\n\n\t\tPolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n\t\tthis.type = 'OctahedronBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tOctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n\tOctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry;\n\n\t/**\n\t * @author timothypratley / https://github.com/timothypratley\n\t */\n\n\tfunction OctahedronGeometry( radius, detail ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'OctahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\tthis.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tOctahedronGeometry.prototype = Object.create( Geometry.prototype );\n\tOctahedronGeometry.prototype.constructor = OctahedronGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction IcosahedronBufferGeometry( radius, detail ) {\n\n\t\tvar t = ( 1 + Math.sqrt( 5 ) ) / 2;\n\n\t\tvar vertices = [\n\t\t\t- 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,\n\t\t\t 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,\n\t\t\t t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1\n\t\t];\n\n\t\tvar indices = [\n\t\t\t 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,\n\t\t\t 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,\n\t\t\t 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,\n\t\t\t 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1\n\t\t];\n\n\t\tPolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n\t\tthis.type = 'IcosahedronBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tIcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n\tIcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry;\n\n\t/**\n\t * @author timothypratley / https://github.com/timothypratley\n\t */\n\n\tfunction IcosahedronGeometry( radius, detail ) {\n\n\t \tGeometry.call( this );\n\n\t\tthis.type = 'IcosahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\tthis.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tIcosahedronGeometry.prototype = Object.create( Geometry.prototype );\n\tIcosahedronGeometry.prototype.constructor = IcosahedronGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction DodecahedronBufferGeometry( radius, detail ) {\n\n\t\tvar t = ( 1 + Math.sqrt( 5 ) ) / 2;\n\t\tvar r = 1 / t;\n\n\t\tvar vertices = [\n\n\t\t\t// (±1, ±1, ±1)\n\t\t\t- 1, - 1, - 1, - 1, - 1, 1,\n\t\t\t- 1, 1, - 1, - 1, 1, 1,\n\t\t\t 1, - 1, - 1, 1, - 1, 1,\n\t\t\t 1, 1, - 1, 1, 1, 1,\n\n\t\t\t// (0, ±1/φ, ±φ)\n\t\t\t 0, - r, - t, 0, - r, t,\n\t\t\t 0, r, - t, 0, r, t,\n\n\t\t\t// (±1/φ, ±φ, 0)\n\t\t\t- r, - t, 0, - r, t, 0,\n\t\t\t r, - t, 0, r, t, 0,\n\n\t\t\t// (±φ, 0, ±1/φ)\n\t\t\t- t, 0, - r, t, 0, - r,\n\t\t\t- t, 0, r, t, 0, r\n\t\t];\n\n\t\tvar indices = [\n\t\t\t 3, 11, 7, 3, 7, 15, 3, 15, 13,\n\t\t\t 7, 19, 17, 7, 17, 6, 7, 6, 15,\n\t\t\t17, 4, 8, 17, 8, 10, 17, 10, 6,\n\t\t\t 8, 0, 16, 8, 16, 2, 8, 2, 10,\n\t\t\t 0, 12, 1, 0, 1, 18, 0, 18, 16,\n\t\t\t 6, 10, 2, 6, 2, 13, 6, 13, 15,\n\t\t\t 2, 16, 18, 2, 18, 3, 2, 3, 13,\n\t\t\t18, 1, 9, 18, 9, 11, 18, 11, 3,\n\t\t\t 4, 14, 12, 4, 12, 0, 4, 0, 8,\n\t\t\t11, 9, 5, 11, 5, 19, 11, 19, 7,\n\t\t\t19, 5, 14, 19, 14, 4, 19, 4, 17,\n\t\t\t 1, 12, 14, 1, 14, 5, 1, 5, 9\n\t\t];\n\n\t\tPolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n\t\tthis.type = 'DodecahedronBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tDodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n\tDodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry;\n\n\t/**\n\t * @author Abe Pazos / https://hamoid.com\n\t */\n\n\tfunction DodecahedronGeometry( radius, detail ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'DodecahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\tthis.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tDodecahedronGeometry.prototype = Object.create( Geometry.prototype );\n\tDodecahedronGeometry.prototype.constructor = DodecahedronGeometry;\n\n\t/**\n\t * @author clockworkgeek / https://github.com/clockworkgeek\n\t * @author timothypratley / https://github.com/timothypratley\n\t * @author WestLangley / http://github.com/WestLangley\n\t*/\n\n\tfunction PolyhedronGeometry( vertices, indices, radius, detail ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'PolyhedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tvertices: vertices,\n\t\t\tindices: indices,\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\tthis.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tPolyhedronGeometry.prototype = Object.create( Geometry.prototype );\n\tPolyhedronGeometry.prototype.constructor = PolyhedronGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t *\n\t * Creates a tube which extrudes along a 3d spline.\n\t *\n\t */\n\n\tfunction TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'TubeBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tpath: path,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tradius: radius,\n\t\t\tradialSegments: radialSegments,\n\t\t\tclosed: closed\n\t\t};\n\n\t\ttubularSegments = tubularSegments || 64;\n\t\tradius = radius || 1;\n\t\tradialSegments = radialSegments || 8;\n\t\tclosed = closed || false;\n\n\t\tvar frames = path.computeFrenetFrames( tubularSegments, closed );\n\n\t\t// expose internals\n\n\t\tthis.tangents = frames.tangents;\n\t\tthis.normals = frames.normals;\n\t\tthis.binormals = frames.binormals;\n\n\t\t// helper variables\n\n\t\tvar vertex = new Vector3();\n\t\tvar normal = new Vector3();\n\t\tvar uv = new Vector2();\n\n\t\tvar i, j;\n\n\t\t// buffer\n\n\t\tvar vertices = [];\n\t\tvar normals = [];\n\t\tvar uvs = [];\n\t\tvar indices = [];\n\n\t\t// create buffer data\n\n\t\tgenerateBufferData();\n\n\t\t// build geometry\n\n\t\tthis.setIndex( ( indices.length > 65535 ? Uint32Attribute : Uint16Attribute )( indices, 1 ) );\n\t\tthis.addAttribute( 'position', Float32Attribute( vertices, 3 ) );\n\t\tthis.addAttribute( 'normal', Float32Attribute( normals, 3 ) );\n\t\tthis.addAttribute( 'uv', Float32Attribute( uvs, 2 ) );\n\n\t\t// functions\n\n\t\tfunction generateBufferData() {\n\n\t\t\tfor ( i = 0; i < tubularSegments; i ++ ) {\n\n\t\t\t\tgenerateSegment( i );\n\n\t\t\t}\n\n\t\t\t// if the geometry is not closed, generate the last row of vertices and normals\n\t\t\t// at the regular position on the given path\n\t\t\t//\n\t\t\t// if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)\n\n\t\t\tgenerateSegment( ( closed === false ) ? tubularSegments : 0 );\n\n\t\t\t// uvs are generated in a separate function.\n\t\t\t// this makes it easy compute correct values for closed geometries\n\n\t\t\tgenerateUVs();\n\n\t\t\t// finally create faces\n\n\t\t\tgenerateIndices();\n\n\t\t}\n\n\t\tfunction generateSegment( i ) {\n\n\t\t\t// we use getPointAt to sample evenly distributed points from the given path\n\n\t\t\tvar P = path.getPointAt( i / tubularSegments );\n\n\t\t\t// retrieve corresponding normal and binormal\n\n\t\t\tvar N = frames.normals[ i ];\n\t\t\tvar B = frames.binormals[ i ];\n\n\t\t\t// generate normals and vertices for the current segment\n\n\t\t\tfor ( j = 0; j <= radialSegments; j ++ ) {\n\n\t\t\t\tvar v = j / radialSegments * Math.PI * 2;\n\n\t\t\t\tvar sin = Math.sin( v );\n\t\t\t\tvar cos = - Math.cos( v );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormal.x = ( cos * N.x + sin * B.x );\n\t\t\t\tnormal.y = ( cos * N.y + sin * B.y );\n\t\t\t\tnormal.z = ( cos * N.z + sin * B.z );\n\t\t\t\tnormal.normalize();\n\n\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = P.x + radius * normal.x;\n\t\t\t\tvertex.y = P.y + radius * normal.y;\n\t\t\t\tvertex.z = P.z + radius * normal.z;\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction generateIndices() {\n\n\t\t\tfor ( j = 1; j <= tubularSegments; j ++ ) {\n\n\t\t\t\tfor ( i = 1; i <= radialSegments; i ++ ) {\n\n\t\t\t\t\tvar a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n\t\t\t\t\tvar b = ( radialSegments + 1 ) * j + ( i - 1 );\n\t\t\t\t\tvar c = ( radialSegments + 1 ) * j + i;\n\t\t\t\t\tvar d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n\t\t\t\t\t// faces\n\n\t\t\t\t\tindices.push( a, b, d );\n\t\t\t\t\tindices.push( b, c, d );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction generateUVs() {\n\n\t\t\tfor ( i = 0; i <= tubularSegments; i ++ ) {\n\n\t\t\t\tfor ( j = 0; j <= radialSegments; j ++ ) {\n\n\t\t\t\t\tuv.x = i / tubularSegments;\n\t\t\t\t\tuv.y = j / radialSegments;\n\n\t\t\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tTubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tTubeBufferGeometry.prototype.constructor = TubeBufferGeometry;\n\n\t/**\n\t * @author oosmoxiecode / https://github.com/oosmoxiecode\n\t * @author WestLangley / https://github.com/WestLangley\n\t * @author zz85 / https://github.com/zz85\n\t * @author miningold / https://github.com/miningold\n\t * @author jonobr1 / https://github.com/jonobr1\n\t *\n\t * Creates a tube which extrudes along a 3d spline.\n\t */\n\n\tfunction TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'TubeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tpath: path,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tradius: radius,\n\t\t\tradialSegments: radialSegments,\n\t\t\tclosed: closed\n\t\t};\n\n\t\tif ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' );\n\n\t\tvar bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed );\n\n\t\t// expose internals\n\n\t\tthis.tangents = bufferGeometry.tangents;\n\t\tthis.normals = bufferGeometry.normals;\n\t\tthis.binormals = bufferGeometry.binormals;\n\n\t\t// create geometry\n\n\t\tthis.fromBufferGeometry( bufferGeometry );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tTubeGeometry.prototype = Object.create( Geometry.prototype );\n\tTubeGeometry.prototype.constructor = TubeGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t *\n\t * see: http://www.blackpawn.com/texts/pqtorus/\n\t */\n\tfunction TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'TorusKnotBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\ttube: tube,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tradialSegments: radialSegments,\n\t\t\tp: p,\n\t\t\tq: q\n\t\t};\n\n\t\tradius = radius || 100;\n\t\ttube = tube || 40;\n\t\ttubularSegments = Math.floor( tubularSegments ) || 64;\n\t\tradialSegments = Math.floor( radialSegments ) || 8;\n\t\tp = p || 2;\n\t\tq = q || 3;\n\n\t\t// used to calculate buffer length\n\t\tvar vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) );\n\t\tvar indexCount = radialSegments * tubularSegments * 2 * 3;\n\n\t\t// buffers\n\t\tvar indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 );\n\t\tvar vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );\n\n\t\t// helper variables\n\t\tvar i, j, index = 0, indexOffset = 0;\n\n\t\tvar vertex = new Vector3();\n\t\tvar normal = new Vector3();\n\t\tvar uv = new Vector2();\n\n\t\tvar P1 = new Vector3();\n\t\tvar P2 = new Vector3();\n\n\t\tvar B = new Vector3();\n\t\tvar T = new Vector3();\n\t\tvar N = new Vector3();\n\n\t\t// generate vertices, normals and uvs\n\n\t\tfor ( i = 0; i <= tubularSegments; ++ i ) {\n\n\t\t\t// the radian \"u\" is used to calculate the position on the torus curve of the current tubular segement\n\n\t\t\tvar u = i / tubularSegments * p * Math.PI * 2;\n\n\t\t\t// now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.\n\t\t\t// these points are used to create a special \"coordinate space\", which is necessary to calculate the correct vertex positions\n\n\t\t\tcalculatePositionOnCurve( u, p, q, radius, P1 );\n\t\t\tcalculatePositionOnCurve( u + 0.01, p, q, radius, P2 );\n\n\t\t\t// calculate orthonormal basis\n\n\t\t\tT.subVectors( P2, P1 );\n\t\t\tN.addVectors( P2, P1 );\n\t\t\tB.crossVectors( T, N );\n\t\t\tN.crossVectors( B, T );\n\n\t\t\t// normalize B, N. T can be ignored, we don't use it\n\n\t\t\tB.normalize();\n\t\t\tN.normalize();\n\n\t\t\tfor ( j = 0; j <= radialSegments; ++ j ) {\n\n\t\t\t\t// now calculate the vertices. they are nothing more than an extrusion of the torus curve.\n\t\t\t\t// because we extrude a shape in the xy-plane, there is no need to calculate a z-value.\n\n\t\t\t\tvar v = j / radialSegments * Math.PI * 2;\n\t\t\t\tvar cx = - tube * Math.cos( v );\n\t\t\t\tvar cy = tube * Math.sin( v );\n\n\t\t\t\t// now calculate the final vertex position.\n\t\t\t\t// first we orient the extrusion with our basis vectos, then we add it to the current position on the curve\n\n\t\t\t\tvertex.x = P1.x + ( cx * N.x + cy * B.x );\n\t\t\t\tvertex.y = P1.y + ( cx * N.y + cy * B.y );\n\t\t\t\tvertex.z = P1.z + ( cx * N.z + cy * B.z );\n\n\t\t\t\t// vertex\n\t\t\t\tvertices.setXYZ( index, vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)\n\t\t\t\tnormal.subVectors( vertex, P1 ).normalize();\n\t\t\t\tnormals.setXYZ( index, normal.x, normal.y, normal.z );\n\n\t\t\t\t// uv\n\t\t\t\tuv.x = i / tubularSegments;\n\t\t\t\tuv.y = j / radialSegments;\n\t\t\t\tuvs.setXY( index, uv.x, uv.y );\n\n\t\t\t\t// increase index\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tfor ( j = 1; j <= tubularSegments; j ++ ) {\n\n\t\t\tfor ( i = 1; i <= radialSegments; i ++ ) {\n\n\t\t\t\t// indices\n\t\t\t\tvar a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n\t\t\t\tvar b = ( radialSegments + 1 ) * j + ( i - 1 );\n\t\t\t\tvar c = ( radialSegments + 1 ) * j + i;\n\t\t\t\tvar d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n\t\t\t\t// face one\n\t\t\t\tindices.setX( indexOffset, a ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, b ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, d ); indexOffset++;\n\n\t\t\t\t// face two\n\t\t\t\tindices.setX( indexOffset, b ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, c ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, d ); indexOffset++;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.addAttribute( 'position', vertices );\n\t\tthis.addAttribute( 'normal', normals );\n\t\tthis.addAttribute( 'uv', uvs );\n\n\t\t// this function calculates the current position on the torus curve\n\n\t\tfunction calculatePositionOnCurve( u, p, q, radius, position ) {\n\n\t\t\tvar cu = Math.cos( u );\n\t\t\tvar su = Math.sin( u );\n\t\t\tvar quOverP = q / p * u;\n\t\t\tvar cs = Math.cos( quOverP );\n\n\t\t\tposition.x = radius * ( 2 + cs ) * 0.5 * cu;\n\t\t\tposition.y = radius * ( 2 + cs ) * su * 0.5;\n\t\t\tposition.z = radius * Math.sin( quOverP ) * 0.5;\n\n\t\t}\n\n\t}\n\n\tTorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tTorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry;\n\n\t/**\n\t * @author oosmoxiecode\n\t */\n\n\tfunction TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'TorusKnotGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\ttube: tube,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tradialSegments: radialSegments,\n\t\t\tp: p,\n\t\t\tq: q\n\t\t};\n\n\t\tif( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' );\n\n\t\tthis.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tTorusKnotGeometry.prototype = Object.create( Geometry.prototype );\n\tTorusKnotGeometry.prototype.constructor = TorusKnotGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'TorusBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\ttube: tube,\n\t\t\tradialSegments: radialSegments,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tarc: arc\n\t\t};\n\n\t\tradius = radius || 100;\n\t\ttube = tube || 40;\n\t\tradialSegments = Math.floor( radialSegments ) || 8;\n\t\ttubularSegments = Math.floor( tubularSegments ) || 6;\n\t\tarc = arc || Math.PI * 2;\n\n\t\t// used to calculate buffer length\n\t\tvar vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) );\n\t\tvar indexCount = radialSegments * tubularSegments * 2 * 3;\n\n\t\t// buffers\n\t\tvar indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount );\n\t\tvar vertices = new Float32Array( vertexCount * 3 );\n\t\tvar normals = new Float32Array( vertexCount * 3 );\n\t\tvar uvs = new Float32Array( vertexCount * 2 );\n\n\t\t// offset variables\n\t\tvar vertexBufferOffset = 0;\n\t\tvar uvBufferOffset = 0;\n\t\tvar indexBufferOffset = 0;\n\n\t\t// helper variables\n\t\tvar center = new Vector3();\n\t\tvar vertex = new Vector3();\n\t\tvar normal = new Vector3();\n\n\t\tvar j, i;\n\n\t\t// generate vertices, normals and uvs\n\n\t\tfor ( j = 0; j <= radialSegments; j ++ ) {\n\n\t\t\tfor ( i = 0; i <= tubularSegments; i ++ ) {\n\n\t\t\t\tvar u = i / tubularSegments * arc;\n\t\t\t\tvar v = j / radialSegments * Math.PI * 2;\n\n\t\t\t\t// vertex\n\t\t\t\tvertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );\n\t\t\t\tvertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );\n\t\t\t\tvertex.z = tube * Math.sin( v );\n\n\t\t\t\tvertices[ vertexBufferOffset ] = vertex.x;\n\t\t\t\tvertices[ vertexBufferOffset + 1 ] = vertex.y;\n\t\t\t\tvertices[ vertexBufferOffset + 2 ] = vertex.z;\n\n\t\t\t\t// this vector is used to calculate the normal\n\t\t\t\tcenter.x = radius * Math.cos( u );\n\t\t\t\tcenter.y = radius * Math.sin( u );\n\n\t\t\t\t// normal\n\t\t\t\tnormal.subVectors( vertex, center ).normalize();\n\n\t\t\t\tnormals[ vertexBufferOffset ] = normal.x;\n\t\t\t\tnormals[ vertexBufferOffset + 1 ] = normal.y;\n\t\t\t\tnormals[ vertexBufferOffset + 2 ] = normal.z;\n\n\t\t\t\t// uv\n\t\t\t\tuvs[ uvBufferOffset ] = i / tubularSegments;\n\t\t\t\tuvs[ uvBufferOffset + 1 ] = j / radialSegments;\n\n\t\t\t\t// update offsets\n\t\t\t\tvertexBufferOffset += 3;\n\t\t\t\tuvBufferOffset += 2;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tfor ( j = 1; j <= radialSegments; j ++ ) {\n\n\t\t\tfor ( i = 1; i <= tubularSegments; i ++ ) {\n\n\t\t\t\t// indices\n\t\t\t\tvar a = ( tubularSegments + 1 ) * j + i - 1;\n\t\t\t\tvar b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;\n\t\t\t\tvar c = ( tubularSegments + 1 ) * ( j - 1 ) + i;\n\t\t\t\tvar d = ( tubularSegments + 1 ) * j + i;\n\n\t\t\t\t// face one\n\t\t\t\tindices[ indexBufferOffset ] = a;\n\t\t\t\tindices[ indexBufferOffset + 1 ] = b;\n\t\t\t\tindices[ indexBufferOffset + 2 ] = d;\n\n\t\t\t\t// face two\n\t\t\t\tindices[ indexBufferOffset + 3 ] = b;\n\t\t\t\tindices[ indexBufferOffset + 4 ] = c;\n\t\t\t\tindices[ indexBufferOffset + 5 ] = d;\n\n\t\t\t\t// update offset\n\t\t\t\tindexBufferOffset += 6;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\t\tthis.setIndex( new BufferAttribute( indices, 1 ) );\n\t\tthis.addAttribute( 'position', new BufferAttribute( vertices, 3 ) );\n\t\tthis.addAttribute( 'normal', new BufferAttribute( normals, 3 ) );\n\t\tthis.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tTorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tTorusBufferGeometry.prototype.constructor = TorusBufferGeometry;\n\n\t/**\n\t * @author oosmoxiecode\n\t * @author mrdoob / http://mrdoob.com/\n\t * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888\n\t */\n\n\tfunction TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'TorusGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\ttube: tube,\n\t\t\tradialSegments: radialSegments,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tarc: arc\n\t\t};\n\n\t\tthis.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) );\n\n\t}\n\n\tTorusGeometry.prototype = Object.create( Geometry.prototype );\n\tTorusGeometry.prototype.constructor = TorusGeometry;\n\n\t/**\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t */\n\n\tvar ShapeUtils = {\n\n\t\t// calculate area of the contour polygon\n\n\t\tarea: function ( contour ) {\n\n\t\t\tvar n = contour.length;\n\t\t\tvar a = 0.0;\n\n\t\t\tfor ( var p = n - 1, q = 0; q < n; p = q ++ ) {\n\n\t\t\t\ta += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;\n\n\t\t\t}\n\n\t\t\treturn a * 0.5;\n\n\t\t},\n\n\t\ttriangulate: ( function () {\n\n\t\t\t/**\n\t\t\t * This code is a quick port of code written in C++ which was submitted to\n\t\t\t * flipcode.com by John W. Ratcliff // July 22, 2000\n\t\t\t * See original code and more information here:\n\t\t\t * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml\n\t\t\t *\n\t\t\t * ported to actionscript by Zevan Rosser\n\t\t\t * www.actionsnippet.com\n\t\t\t *\n\t\t\t * ported to javascript by Joshua Koo\n\t\t\t * http://www.lab4games.net/zz85/blog\n\t\t\t *\n\t\t\t */\n\n\t\t\tfunction snip( contour, u, v, w, n, verts ) {\n\n\t\t\t\tvar p;\n\t\t\t\tvar ax, ay, bx, by;\n\t\t\t\tvar cx, cy, px, py;\n\n\t\t\t\tax = contour[ verts[ u ] ].x;\n\t\t\t\tay = contour[ verts[ u ] ].y;\n\n\t\t\t\tbx = contour[ verts[ v ] ].x;\n\t\t\t\tby = contour[ verts[ v ] ].y;\n\n\t\t\t\tcx = contour[ verts[ w ] ].x;\n\t\t\t\tcy = contour[ verts[ w ] ].y;\n\n\t\t\t\tif ( ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax ) <= 0 ) return false;\n\n\t\t\t\tvar aX, aY, bX, bY, cX, cY;\n\t\t\t\tvar apx, apy, bpx, bpy, cpx, cpy;\n\t\t\t\tvar cCROSSap, bCROSScp, aCROSSbp;\n\n\t\t\t\taX = cx - bx; aY = cy - by;\n\t\t\t\tbX = ax - cx; bY = ay - cy;\n\t\t\t\tcX = bx - ax; cY = by - ay;\n\n\t\t\t\tfor ( p = 0; p < n; p ++ ) {\n\n\t\t\t\t\tpx = contour[ verts[ p ] ].x;\n\t\t\t\t\tpy = contour[ verts[ p ] ].y;\n\n\t\t\t\t\tif ( ( ( px === ax ) && ( py === ay ) ) ||\n\t\t\t\t\t\t ( ( px === bx ) && ( py === by ) ) ||\n\t\t\t\t\t\t ( ( px === cx ) && ( py === cy ) ) )\tcontinue;\n\n\t\t\t\t\tapx = px - ax; apy = py - ay;\n\t\t\t\t\tbpx = px - bx; bpy = py - by;\n\t\t\t\t\tcpx = px - cx; cpy = py - cy;\n\n\t\t\t\t\t// see if p is inside triangle abc\n\n\t\t\t\t\taCROSSbp = aX * bpy - aY * bpx;\n\t\t\t\t\tcCROSSap = cX * apy - cY * apx;\n\t\t\t\t\tbCROSScp = bX * cpy - bY * cpx;\n\n\t\t\t\t\tif ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false;\n\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\n\t\t\t}\n\n\t\t\t// takes in an contour array and returns\n\n\t\t\treturn function triangulate( contour, indices ) {\n\n\t\t\t\tvar n = contour.length;\n\n\t\t\t\tif ( n < 3 ) return null;\n\n\t\t\t\tvar result = [],\n\t\t\t\t\tverts = [],\n\t\t\t\t\tvertIndices = [];\n\n\t\t\t\t/* we want a counter-clockwise polygon in verts */\n\n\t\t\t\tvar u, v, w;\n\n\t\t\t\tif ( ShapeUtils.area( contour ) > 0.0 ) {\n\n\t\t\t\t\tfor ( v = 0; v < n; v ++ ) verts[ v ] = v;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tfor ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;\n\n\t\t\t\t}\n\n\t\t\t\tvar nv = n;\n\n\t\t\t\t/* remove nv - 2 vertices, creating 1 triangle every time */\n\n\t\t\t\tvar count = 2 * nv; /* error detection */\n\n\t\t\t\tfor ( v = nv - 1; nv > 2; ) {\n\n\t\t\t\t\t/* if we loop, it is probably a non-simple polygon */\n\n\t\t\t\t\tif ( ( count -- ) <= 0 ) {\n\n\t\t\t\t\t\t//** Triangulate: ERROR - probable bad polygon!\n\n\t\t\t\t\t\t//throw ( \"Warning, unable to triangulate polygon!\" );\n\t\t\t\t\t\t//return null;\n\t\t\t\t\t\t// Sometimes warning is fine, especially polygons are triangulated in reverse.\n\t\t\t\t\t\tconsole.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' );\n\n\t\t\t\t\t\tif ( indices ) return vertIndices;\n\t\t\t\t\t\treturn result;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t/* three consecutive vertices in current polygon, <u,v,w> */\n\n\t\t\t\t\tu = v; \t \tif ( nv <= u ) u = 0; /* previous */\n\t\t\t\t\tv = u + 1; if ( nv <= v ) v = 0; /* new v */\n\t\t\t\t\tw = v + 1; if ( nv <= w ) w = 0; /* next */\n\n\t\t\t\t\tif ( snip( contour, u, v, w, nv, verts ) ) {\n\n\t\t\t\t\t\tvar a, b, c, s, t;\n\n\t\t\t\t\t\t/* true names of the vertices */\n\n\t\t\t\t\t\ta = verts[ u ];\n\t\t\t\t\t\tb = verts[ v ];\n\t\t\t\t\t\tc = verts[ w ];\n\n\t\t\t\t\t\t/* output Triangle */\n\n\t\t\t\t\t\tresult.push( [ contour[ a ],\n\t\t\t\t\t\t\tcontour[ b ],\n\t\t\t\t\t\t\tcontour[ c ] ] );\n\n\n\t\t\t\t\t\tvertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );\n\n\t\t\t\t\t\t/* remove v from the remaining polygon */\n\n\t\t\t\t\t\tfor ( s = v, t = v + 1; t < nv; s ++, t ++ ) {\n\n\t\t\t\t\t\t\tverts[ s ] = verts[ t ];\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnv --;\n\n\t\t\t\t\t\t/* reset error detection counter */\n\n\t\t\t\t\t\tcount = 2 * nv;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( indices ) return vertIndices;\n\t\t\t\treturn result;\n\n\t\t\t}\n\n\t\t} )(),\n\n\t\ttriangulateShape: function ( contour, holes ) {\n\n\t\t\tfunction removeDupEndPts(points) {\n\n\t\t\t\tvar l = points.length;\n\n\t\t\t\tif ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {\n\n\t\t\t\t\tpoints.pop();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tremoveDupEndPts( contour );\n\t\t\tholes.forEach( removeDupEndPts );\n\n\t\t\tfunction point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {\n\n\t\t\t\t// inOtherPt needs to be collinear to the inSegment\n\t\t\t\tif ( inSegPt1.x !== inSegPt2.x ) {\n\n\t\t\t\t\tif ( inSegPt1.x < inSegPt2.x ) {\n\n\t\t\t\t\t\treturn\t( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\treturn\t( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( inSegPt1.y < inSegPt2.y ) {\n\n\t\t\t\t\t\treturn\t( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\treturn\t( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {\n\n\t\t\t\tvar seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;\n\t\t\t\tvar seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;\n\n\t\t\t\tvar seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;\n\t\t\t\tvar seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;\n\n\t\t\t\tvar limit\t\t= seg1dy * seg2dx - seg1dx * seg2dy;\n\t\t\t\tvar perpSeg1\t= seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;\n\n\t\t\t\tif ( Math.abs( limit ) > Number.EPSILON ) {\n\n\t\t\t\t\t// not parallel\n\n\t\t\t\t\tvar perpSeg2;\n\t\t\t\t\tif ( limit > 0 ) {\n\n\t\t\t\t\t\tif ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) \t\treturn [];\n\t\t\t\t\t\tperpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;\n\t\t\t\t\t\tif ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) \t\treturn [];\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) \t\treturn [];\n\t\t\t\t\t\tperpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;\n\t\t\t\t\t\tif ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) \t\treturn [];\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// i.e. to reduce rounding errors\n\t\t\t\t\t// intersection at endpoint of segment#1?\n\t\t\t\t\tif ( perpSeg2 === 0 ) {\n\n\t\t\t\t\t\tif ( ( inExcludeAdjacentSegs ) &&\n\t\t\t\t\t\t\t ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) )\t\treturn [];\n\t\t\t\t\t\treturn [ inSeg1Pt1 ];\n\n\t\t\t\t\t}\n\t\t\t\t\tif ( perpSeg2 === limit ) {\n\n\t\t\t\t\t\tif ( ( inExcludeAdjacentSegs ) &&\n\t\t\t\t\t\t\t ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) )\t\treturn [];\n\t\t\t\t\t\treturn [ inSeg1Pt2 ];\n\n\t\t\t\t\t}\n\t\t\t\t\t// intersection at endpoint of segment#2?\n\t\t\t\t\tif ( perpSeg1 === 0 )\t\treturn [ inSeg2Pt1 ];\n\t\t\t\t\tif ( perpSeg1 === limit )\treturn [ inSeg2Pt2 ];\n\n\t\t\t\t\t// return real intersection point\n\t\t\t\t\tvar factorSeg1 = perpSeg2 / limit;\n\t\t\t\t\treturn\t[ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,\n\t\t\t\t\t\t\t\ty: inSeg1Pt1.y + factorSeg1 * seg1dy } ];\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// parallel or collinear\n\t\t\t\t\tif ( ( perpSeg1 !== 0 ) ||\n\t\t\t\t\t\t ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) \t\t\treturn [];\n\n\t\t\t\t\t// they are collinear or degenerate\n\t\t\t\t\tvar seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) );\t// segment1 is just a point?\n\t\t\t\t\tvar seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) );\t// segment2 is just a point?\n\t\t\t\t\t// both segments are points\n\t\t\t\t\tif ( seg1Pt && seg2Pt ) {\n\n\t\t\t\t\t\tif ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) ||\n\t\t\t\t\t\t\t ( inSeg1Pt1.y !== inSeg2Pt1.y ) )\t\treturn [];\t// they are distinct points\n\t\t\t\t\t\treturn [ inSeg1Pt1 ]; \t\t\t\t\t\t// they are the same point\n\n\t\t\t\t\t}\n\t\t\t\t\t// segment#1 is a single point\n\t\t\t\t\tif ( seg1Pt ) {\n\n\t\t\t\t\t\tif ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) )\t\treturn [];\t\t// but not in segment#2\n\t\t\t\t\t\treturn [ inSeg1Pt1 ];\n\n\t\t\t\t\t}\n\t\t\t\t\t// segment#2 is a single point\n\t\t\t\t\tif ( seg2Pt ) {\n\n\t\t\t\t\t\tif ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) )\t\treturn [];\t\t// but not in segment#1\n\t\t\t\t\t\treturn [ inSeg2Pt1 ];\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// they are collinear segments, which might overlap\n\t\t\t\t\tvar seg1min, seg1max, seg1minVal, seg1maxVal;\n\t\t\t\t\tvar seg2min, seg2max, seg2minVal, seg2maxVal;\n\t\t\t\t\tif ( seg1dx !== 0 ) {\n\n\t\t\t\t\t\t// the segments are NOT on a vertical line\n\t\t\t\t\t\tif ( inSeg1Pt1.x < inSeg1Pt2.x ) {\n\n\t\t\t\t\t\t\tseg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;\n\t\t\t\t\t\t\tseg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tseg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;\n\t\t\t\t\t\t\tseg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( inSeg2Pt1.x < inSeg2Pt2.x ) {\n\n\t\t\t\t\t\t\tseg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;\n\t\t\t\t\t\t\tseg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tseg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;\n\t\t\t\t\t\t\tseg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// the segments are on a vertical line\n\t\t\t\t\t\tif ( inSeg1Pt1.y < inSeg1Pt2.y ) {\n\n\t\t\t\t\t\t\tseg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;\n\t\t\t\t\t\t\tseg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tseg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;\n\t\t\t\t\t\t\tseg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( inSeg2Pt1.y < inSeg2Pt2.y ) {\n\n\t\t\t\t\t\t\tseg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;\n\t\t\t\t\t\t\tseg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tseg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;\n\t\t\t\t\t\t\tseg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t\tif ( seg1minVal <= seg2minVal ) {\n\n\t\t\t\t\t\tif ( seg1maxVal < seg2minVal )\treturn [];\n\t\t\t\t\t\tif ( seg1maxVal === seg2minVal )\t{\n\n\t\t\t\t\t\t\tif ( inExcludeAdjacentSegs )\t\treturn [];\n\t\t\t\t\t\t\treturn [ seg2min ];\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( seg1maxVal <= seg2maxVal )\treturn [ seg2min, seg1max ];\n\t\t\t\t\t\treturn\t[ seg2min, seg2max ];\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( seg1minVal > seg2maxVal )\treturn [];\n\t\t\t\t\t\tif ( seg1minVal === seg2maxVal )\t{\n\n\t\t\t\t\t\t\tif ( inExcludeAdjacentSegs )\t\treturn [];\n\t\t\t\t\t\t\treturn [ seg1min ];\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( seg1maxVal <= seg2maxVal )\treturn [ seg1min, seg1max ];\n\t\t\t\t\t\treturn\t[ seg1min, seg2max ];\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {\n\n\t\t\t\t// The order of legs is important\n\n\t\t\t\t// translation of all points, so that Vertex is at (0,0)\n\t\t\t\tvar legFromPtX\t= inLegFromPt.x - inVertex.x, legFromPtY\t= inLegFromPt.y - inVertex.y;\n\t\t\t\tvar legToPtX\t= inLegToPt.x\t- inVertex.x, legToPtY\t\t= inLegToPt.y\t- inVertex.y;\n\t\t\t\tvar otherPtX\t= inOtherPt.x\t- inVertex.x, otherPtY\t\t= inOtherPt.y\t- inVertex.y;\n\n\t\t\t\t// main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.\n\t\t\t\tvar from2toAngle\t= legFromPtX * legToPtY - legFromPtY * legToPtX;\n\t\t\t\tvar from2otherAngle\t= legFromPtX * otherPtY - legFromPtY * otherPtX;\n\n\t\t\t\tif ( Math.abs( from2toAngle ) > Number.EPSILON ) {\n\n\t\t\t\t\t// angle != 180 deg.\n\n\t\t\t\t\tvar other2toAngle\t\t= otherPtX * legToPtY - otherPtY * legToPtX;\n\t\t\t\t\t// console.log( \"from2to: \" + from2toAngle + \", from2other: \" + from2otherAngle + \", other2to: \" + other2toAngle );\n\n\t\t\t\t\tif ( from2toAngle > 0 ) {\n\n\t\t\t\t\t\t// main angle < 180 deg.\n\t\t\t\t\t\treturn\t( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// main angle > 180 deg.\n\t\t\t\t\t\treturn\t( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// angle == 180 deg.\n\t\t\t\t\t// console.log( \"from2to: 180 deg., from2other: \" + from2otherAngle );\n\t\t\t\t\treturn\t( from2otherAngle > 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\n\t\t\tfunction removeHoles( contour, holes ) {\n\n\t\t\t\tvar shape = contour.concat(); // work on this shape\n\t\t\t\tvar hole;\n\n\t\t\t\tfunction isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {\n\n\t\t\t\t\t// Check if hole point lies within angle around shape point\n\t\t\t\t\tvar lastShapeIdx = shape.length - 1;\n\n\t\t\t\t\tvar prevShapeIdx = inShapeIdx - 1;\n\t\t\t\t\tif ( prevShapeIdx < 0 )\t\t\tprevShapeIdx = lastShapeIdx;\n\n\t\t\t\t\tvar nextShapeIdx = inShapeIdx + 1;\n\t\t\t\t\tif ( nextShapeIdx > lastShapeIdx )\tnextShapeIdx = 0;\n\n\t\t\t\t\tvar insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] );\n\t\t\t\t\tif ( ! insideAngle ) {\n\n\t\t\t\t\t\t// console.log( \"Vertex (Shape): \" + inShapeIdx + \", Point: \" + hole[inHoleIdx].x + \"/\" + hole[inHoleIdx].y );\n\t\t\t\t\t\treturn\tfalse;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if shape point lies within angle around hole point\n\t\t\t\t\tvar lastHoleIdx = hole.length - 1;\n\n\t\t\t\t\tvar prevHoleIdx = inHoleIdx - 1;\n\t\t\t\t\tif ( prevHoleIdx < 0 )\t\t\tprevHoleIdx = lastHoleIdx;\n\n\t\t\t\t\tvar nextHoleIdx = inHoleIdx + 1;\n\t\t\t\t\tif ( nextHoleIdx > lastHoleIdx )\tnextHoleIdx = 0;\n\n\t\t\t\t\tinsideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] );\n\t\t\t\t\tif ( ! insideAngle ) {\n\n\t\t\t\t\t\t// console.log( \"Vertex (Hole): \" + inHoleIdx + \", Point: \" + shape[inShapeIdx].x + \"/\" + shape[inShapeIdx].y );\n\t\t\t\t\t\treturn\tfalse;\n\n\t\t\t\t\t}\n\n\t\t\t\t\treturn\ttrue;\n\n\t\t\t\t}\n\n\t\t\t\tfunction intersectsShapeEdge( inShapePt, inHolePt ) {\n\n\t\t\t\t\t// checks for intersections with shape edges\n\t\t\t\t\tvar sIdx, nextIdx, intersection;\n\t\t\t\t\tfor ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {\n\n\t\t\t\t\t\tnextIdx = sIdx + 1; nextIdx %= shape.length;\n\t\t\t\t\t\tintersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true );\n\t\t\t\t\t\tif ( intersection.length > 0 )\t\treturn\ttrue;\n\n\t\t\t\t\t}\n\n\t\t\t\t\treturn\tfalse;\n\n\t\t\t\t}\n\n\t\t\t\tvar indepHoles = [];\n\n\t\t\t\tfunction intersectsHoleEdge( inShapePt, inHolePt ) {\n\n\t\t\t\t\t// checks for intersections with hole edges\n\t\t\t\t\tvar ihIdx, chkHole,\n\t\t\t\t\t\thIdx, nextIdx, intersection;\n\t\t\t\t\tfor ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {\n\n\t\t\t\t\t\tchkHole = holes[ indepHoles[ ihIdx ]];\n\t\t\t\t\t\tfor ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {\n\n\t\t\t\t\t\t\tnextIdx = hIdx + 1; nextIdx %= chkHole.length;\n\t\t\t\t\t\t\tintersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true );\n\t\t\t\t\t\t\tif ( intersection.length > 0 )\t\treturn\ttrue;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t\treturn\tfalse;\n\n\t\t\t\t}\n\n\t\t\t\tvar holeIndex, shapeIndex,\n\t\t\t\t\tshapePt, holePt,\n\t\t\t\t\tholeIdx, cutKey, failedCuts = [],\n\t\t\t\t\ttmpShape1, tmpShape2,\n\t\t\t\t\ttmpHole1, tmpHole2;\n\n\t\t\t\tfor ( var h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\t\tindepHoles.push( h );\n\n\t\t\t\t}\n\n\t\t\t\tvar minShapeIndex = 0;\n\t\t\t\tvar counter = indepHoles.length * 2;\n\t\t\t\twhile ( indepHoles.length > 0 ) {\n\n\t\t\t\t\tcounter --;\n\t\t\t\t\tif ( counter < 0 ) {\n\n\t\t\t\t\t\tconsole.log( \"Infinite Loop! Holes left:\" + indepHoles.length + \", Probably Hole outside Shape!\" );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// search for shape-vertex and hole-vertex,\n\t\t\t\t\t// which can be connected without intersections\n\t\t\t\t\tfor ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {\n\n\t\t\t\t\t\tshapePt = shape[ shapeIndex ];\n\t\t\t\t\t\tholeIndex\t= - 1;\n\n\t\t\t\t\t\t// search for hole which can be reached without intersections\n\t\t\t\t\t\tfor ( var h = 0; h < indepHoles.length; h ++ ) {\n\n\t\t\t\t\t\t\tholeIdx = indepHoles[ h ];\n\n\t\t\t\t\t\t\t// prevent multiple checks\n\t\t\t\t\t\t\tcutKey = shapePt.x + \":\" + shapePt.y + \":\" + holeIdx;\n\t\t\t\t\t\t\tif ( failedCuts[ cutKey ] !== undefined )\t\t\tcontinue;\n\n\t\t\t\t\t\t\thole = holes[ holeIdx ];\n\t\t\t\t\t\t\tfor ( var h2 = 0; h2 < hole.length; h2 ++ ) {\n\n\t\t\t\t\t\t\t\tholePt = hole[ h2 ];\n\t\t\t\t\t\t\t\tif ( ! isCutLineInsideAngles( shapeIndex, h2 ) )\t\tcontinue;\n\t\t\t\t\t\t\t\tif ( intersectsShapeEdge( shapePt, holePt ) )\t\tcontinue;\n\t\t\t\t\t\t\t\tif ( intersectsHoleEdge( shapePt, holePt ) )\t\tcontinue;\n\n\t\t\t\t\t\t\t\tholeIndex = h2;\n\t\t\t\t\t\t\t\tindepHoles.splice( h, 1 );\n\n\t\t\t\t\t\t\t\ttmpShape1 = shape.slice( 0, shapeIndex + 1 );\n\t\t\t\t\t\t\t\ttmpShape2 = shape.slice( shapeIndex );\n\t\t\t\t\t\t\t\ttmpHole1 = hole.slice( holeIndex );\n\t\t\t\t\t\t\t\ttmpHole2 = hole.slice( 0, holeIndex + 1 );\n\n\t\t\t\t\t\t\t\tshape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );\n\n\t\t\t\t\t\t\t\tminShapeIndex = shapeIndex;\n\n\t\t\t\t\t\t\t\t// Debug only, to show the selected cuts\n\t\t\t\t\t\t\t\t// glob_CutLines.push( [ shapePt, holePt ] );\n\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif ( holeIndex >= 0 )\tbreak;\t\t// hole-vertex found\n\n\t\t\t\t\t\t\tfailedCuts[ cutKey ] = true;\t\t\t// remember failure\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( holeIndex >= 0 )\tbreak;\t\t// hole-vertex found\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn shape; \t\t\t/* shape with no holes */\n\n\t\t\t}\n\n\n\t\t\tvar i, il, f, face,\n\t\t\t\tkey, index,\n\t\t\t\tallPointsMap = {};\n\n\t\t\t// To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.\n\n\t\t\tvar allpoints = contour.concat();\n\n\t\t\tfor ( var h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\tArray.prototype.push.apply( allpoints, holes[ h ] );\n\n\t\t\t}\n\n\t\t\t//console.log( \"allpoints\",allpoints, allpoints.length );\n\n\t\t\t// prepare all points map\n\n\t\t\tfor ( i = 0, il = allpoints.length; i < il; i ++ ) {\n\n\t\t\t\tkey = allpoints[ i ].x + \":\" + allpoints[ i ].y;\n\n\t\t\t\tif ( allPointsMap[ key ] !== undefined ) {\n\n\t\t\t\t\tconsole.warn( \"THREE.ShapeUtils: Duplicate point\", key, i );\n\n\t\t\t\t}\n\n\t\t\t\tallPointsMap[ key ] = i;\n\n\t\t\t}\n\n\t\t\t// remove holes by cutting paths to holes and adding them to the shape\n\t\t\tvar shapeWithoutHoles = removeHoles( contour, holes );\n\n\t\t\tvar triangles = ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape\n\t\t\t//console.log( \"triangles\",triangles, triangles.length );\n\n\t\t\t// check all face vertices against all points map\n\n\t\t\tfor ( i = 0, il = triangles.length; i < il; i ++ ) {\n\n\t\t\t\tface = triangles[ i ];\n\n\t\t\t\tfor ( f = 0; f < 3; f ++ ) {\n\n\t\t\t\t\tkey = face[ f ].x + \":\" + face[ f ].y;\n\n\t\t\t\t\tindex = allPointsMap[ key ];\n\n\t\t\t\t\tif ( index !== undefined ) {\n\n\t\t\t\t\t\tface[ f ] = index;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn triangles.concat();\n\n\t\t},\n\n\t\tisClockWise: function ( pts ) {\n\n\t\t\treturn ShapeUtils.area( pts ) < 0;\n\n\t\t},\n\n\t\t// Bezier Curves formulas obtained from\n\t\t// http://en.wikipedia.org/wiki/B%C3%A9zier_curve\n\n\t\t// Quad Bezier Functions\n\n\t\tb2: ( function () {\n\n\t\t\tfunction b2p0( t, p ) {\n\n\t\t\t\tvar k = 1 - t;\n\t\t\t\treturn k * k * p;\n\n\t\t\t}\n\n\t\t\tfunction b2p1( t, p ) {\n\n\t\t\t\treturn 2 * ( 1 - t ) * t * p;\n\n\t\t\t}\n\n\t\t\tfunction b2p2( t, p ) {\n\n\t\t\t\treturn t * t * p;\n\n\t\t\t}\n\n\t\t\treturn function b2( t, p0, p1, p2 ) {\n\n\t\t\t\treturn b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 );\n\n\t\t\t};\n\n\t\t} )(),\n\n\t\t// Cubic Bezier Functions\n\n\t\tb3: ( function () {\n\n\t\t\tfunction b3p0( t, p ) {\n\n\t\t\t\tvar k = 1 - t;\n\t\t\t\treturn k * k * k * p;\n\n\t\t\t}\n\n\t\t\tfunction b3p1( t, p ) {\n\n\t\t\t\tvar k = 1 - t;\n\t\t\t\treturn 3 * k * k * t * p;\n\n\t\t\t}\n\n\t\t\tfunction b3p2( t, p ) {\n\n\t\t\t\tvar k = 1 - t;\n\t\t\t\treturn 3 * k * t * t * p;\n\n\t\t\t}\n\n\t\t\tfunction b3p3( t, p ) {\n\n\t\t\t\treturn t * t * t * p;\n\n\t\t\t}\n\n\t\t\treturn function b3( t, p0, p1, p2, p3 ) {\n\n\t\t\t\treturn b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 );\n\n\t\t\t};\n\n\t\t} )()\n\n\t};\n\n\t/**\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t *\n\t * Creates extruded geometry from a path shape.\n\t *\n\t * parameters = {\n\t *\n\t * curveSegments: <int>, // number of points on the curves\n\t * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too\n\t * amount: <int>, // Depth to extrude the shape\n\t *\n\t * bevelEnabled: <bool>, // turn on bevel\n\t * bevelThickness: <float>, // how deep into the original shape bevel goes\n\t * bevelSize: <float>, // how far from shape outline is bevel\n\t * bevelSegments: <int>, // number of bevel layers\n\t *\n\t * extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)\n\t * frames: <Object> // containing arrays of tangents, normals, binormals\n\t *\n\t * uvGenerator: <Object> // object that provides UV generator functions\n\t *\n\t * }\n\t **/\n\n\tfunction ExtrudeGeometry( shapes, options ) {\n\n\t\tif ( typeof( shapes ) === \"undefined\" ) {\n\n\t\t\tshapes = [];\n\t\t\treturn;\n\n\t\t}\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'ExtrudeGeometry';\n\n\t\tshapes = Array.isArray( shapes ) ? shapes : [ shapes ];\n\n\t\tthis.addShapeList( shapes, options );\n\n\t\tthis.computeFaceNormals();\n\n\t\t// can't really use automatic vertex normals\n\t\t// as then front and back sides get smoothed too\n\t\t// should do separate smoothing just for sides\n\n\t\t//this.computeVertexNormals();\n\n\t\t//console.log( \"took\", ( Date.now() - startTime ) );\n\n\t}\n\n\tExtrudeGeometry.prototype = Object.create( Geometry.prototype );\n\tExtrudeGeometry.prototype.constructor = ExtrudeGeometry;\n\n\tExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {\n\n\t\tvar sl = shapes.length;\n\n\t\tfor ( var s = 0; s < sl; s ++ ) {\n\n\t\t\tvar shape = shapes[ s ];\n\t\t\tthis.addShape( shape, options );\n\n\t\t}\n\n\t};\n\n\tExtrudeGeometry.prototype.addShape = function ( shape, options ) {\n\n\t\tvar amount = options.amount !== undefined ? options.amount : 100;\n\n\t\tvar bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10\n\t\tvar bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8\n\t\tvar bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;\n\n\t\tvar bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false\n\n\t\tvar curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;\n\n\t\tvar steps = options.steps !== undefined ? options.steps : 1;\n\n\t\tvar extrudePath = options.extrudePath;\n\t\tvar extrudePts, extrudeByPath = false;\n\n\t\t// Use default WorldUVGenerator if no UV generators are specified.\n\t\tvar uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator;\n\n\t\tvar splineTube, binormal, normal, position2;\n\t\tif ( extrudePath ) {\n\n\t\t\textrudePts = extrudePath.getSpacedPoints( steps );\n\n\t\t\textrudeByPath = true;\n\t\t\tbevelEnabled = false; // bevels not supported for path extrusion\n\n\t\t\t// SETUP TNB variables\n\n\t\t\t// TODO1 - have a .isClosed in spline?\n\n\t\t\tsplineTube = options.frames !== undefined ? options.frames : extrudePath.computeFrenetFrames( steps, false );\n\n\t\t\t// console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);\n\n\t\t\tbinormal = new Vector3();\n\t\t\tnormal = new Vector3();\n\t\t\tposition2 = new Vector3();\n\n\t\t}\n\n\t\t// Safeguards if bevels are not enabled\n\n\t\tif ( ! bevelEnabled ) {\n\n\t\t\tbevelSegments = 0;\n\t\t\tbevelThickness = 0;\n\t\t\tbevelSize = 0;\n\n\t\t}\n\n\t\t// Variables initialization\n\n\t\tvar ahole, h, hl; // looping of holes\n\t\tvar scope = this;\n\n\t\tvar shapesOffset = this.vertices.length;\n\n\t\tvar shapePoints = shape.extractPoints( curveSegments );\n\n\t\tvar vertices = shapePoints.shape;\n\t\tvar holes = shapePoints.holes;\n\n\t\tvar reverse = ! ShapeUtils.isClockWise( vertices );\n\n\t\tif ( reverse ) {\n\n\t\t\tvertices = vertices.reverse();\n\n\t\t\t// Maybe we should also check if holes are in the opposite direction, just to be safe ...\n\n\t\t\tfor ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\tahole = holes[ h ];\n\n\t\t\t\tif ( ShapeUtils.isClockWise( ahole ) ) {\n\n\t\t\t\t\tholes[ h ] = ahole.reverse();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!\n\n\t\t}\n\n\n\t\tvar faces = ShapeUtils.triangulateShape( vertices, holes );\n\n\t\t/* Vertices */\n\n\t\tvar contour = vertices; // vertices has all points but contour has only points of circumference\n\n\t\tfor ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\tahole = holes[ h ];\n\n\t\t\tvertices = vertices.concat( ahole );\n\n\t\t}\n\n\n\t\tfunction scalePt2( pt, vec, size ) {\n\n\t\t\tif ( ! vec ) console.error( \"THREE.ExtrudeGeometry: vec does not exist\" );\n\n\t\t\treturn vec.clone().multiplyScalar( size ).add( pt );\n\n\t\t}\n\n\t\tvar b, bs, t, z,\n\t\t\tvert, vlen = vertices.length,\n\t\t\tface, flen = faces.length;\n\n\n\t\t// Find directions for point movement\n\n\n\t\tfunction getBevelVec( inPt, inPrev, inNext ) {\n\n\t\t\t// computes for inPt the corresponding point inPt' on a new contour\n\t\t\t// shifted by 1 unit (length of normalized vector) to the left\n\t\t\t// if we walk along contour clockwise, this new contour is outside the old one\n\t\t\t//\n\t\t\t// inPt' is the intersection of the two lines parallel to the two\n\t\t\t// adjacent edges of inPt at a distance of 1 unit on the left side.\n\n\t\t\tvar v_trans_x, v_trans_y, shrink_by = 1;\t\t// resulting translation vector for inPt\n\n\t\t\t// good reading for geometry algorithms (here: line-line intersection)\n\t\t\t// http://geomalgorithms.com/a05-_intersect-1.html\n\n\t\t\tvar v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;\n\t\t\tvar v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;\n\n\t\t\tvar v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );\n\n\t\t\t// check for collinear edges\n\t\t\tvar collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n\t\t\tif ( Math.abs( collinear0 ) > Number.EPSILON ) {\n\n\t\t\t\t// not collinear\n\n\t\t\t\t// length of vectors for normalizing\n\n\t\t\t\tvar v_prev_len = Math.sqrt( v_prev_lensq );\n\t\t\t\tvar v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );\n\n\t\t\t\t// shift adjacent points by unit vectors to the left\n\n\t\t\t\tvar ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );\n\t\t\t\tvar ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );\n\n\t\t\t\tvar ptNextShift_x = ( inNext.x - v_next_y / v_next_len );\n\t\t\t\tvar ptNextShift_y = ( inNext.y + v_next_x / v_next_len );\n\n\t\t\t\t// scaling factor for v_prev to intersection point\n\n\t\t\t\tvar sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -\n\t\t\t\t\t\t\t( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /\n\t\t\t\t\t\t ( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n\t\t\t\t// vector from inPt to intersection point\n\n\t\t\t\tv_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );\n\t\t\t\tv_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );\n\n\t\t\t\t// Don't normalize!, otherwise sharp corners become ugly\n\t\t\t\t// but prevent crazy spikes\n\t\t\t\tvar v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );\n\t\t\t\tif ( v_trans_lensq <= 2 ) {\n\n\t\t\t\t\treturn\tnew Vector2( v_trans_x, v_trans_y );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tshrink_by = Math.sqrt( v_trans_lensq / 2 );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// handle special case of collinear edges\n\n\t\t\t\tvar direction_eq = false;\t\t// assumes: opposite\n\t\t\t\tif ( v_prev_x > Number.EPSILON ) {\n\n\t\t\t\t\tif ( v_next_x > Number.EPSILON ) {\n\n\t\t\t\t\t\tdirection_eq = true;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( v_prev_x < - Number.EPSILON ) {\n\n\t\t\t\t\t\tif ( v_next_x < - Number.EPSILON ) {\n\n\t\t\t\t\t\t\tdirection_eq = true;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {\n\n\t\t\t\t\t\t\tdirection_eq = true;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( direction_eq ) {\n\n\t\t\t\t\t// console.log(\"Warning: lines are a straight sequence\");\n\t\t\t\t\tv_trans_x = - v_prev_y;\n\t\t\t\t\tv_trans_y = v_prev_x;\n\t\t\t\t\tshrink_by = Math.sqrt( v_prev_lensq );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// console.log(\"Warning: lines are a straight spike\");\n\t\t\t\t\tv_trans_x = v_prev_x;\n\t\t\t\t\tv_trans_y = v_prev_y;\n\t\t\t\t\tshrink_by = Math.sqrt( v_prev_lensq / 2 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn\tnew Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );\n\n\t\t}\n\n\n\t\tvar contourMovements = [];\n\n\t\tfor ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n\t\t\tif ( j === il ) j = 0;\n\t\t\tif ( k === il ) k = 0;\n\n\t\t\t// (j)---(i)---(k)\n\t\t\t// console.log('i,j,k', i, j , k)\n\n\t\t\tcontourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );\n\n\t\t}\n\n\t\tvar holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();\n\n\t\tfor ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\tahole = holes[ h ];\n\n\t\t\toneHoleMovements = [];\n\n\t\t\tfor ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n\t\t\t\tif ( j === il ) j = 0;\n\t\t\t\tif ( k === il ) k = 0;\n\n\t\t\t\t// (j)---(i)---(k)\n\t\t\t\toneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );\n\n\t\t\t}\n\n\t\t\tholesMovements.push( oneHoleMovements );\n\t\t\tverticesMovements = verticesMovements.concat( oneHoleMovements );\n\n\t\t}\n\n\n\t\t// Loop bevelSegments, 1 for the front, 1 for the back\n\n\t\tfor ( b = 0; b < bevelSegments; b ++ ) {\n\n\t\t\t//for ( b = bevelSegments; b > 0; b -- ) {\n\n\t\t\tt = b / bevelSegments;\n\t\t\tz = bevelThickness * Math.cos( t * Math.PI / 2 );\n\t\t\tbs = bevelSize * Math.sin( t * Math.PI / 2 );\n\n\t\t\t// contract shape\n\n\t\t\tfor ( i = 0, il = contour.length; i < il; i ++ ) {\n\n\t\t\t\tvert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n\n\t\t\t\tv( vert.x, vert.y, - z );\n\n\t\t\t}\n\n\t\t\t// expand holes\n\n\t\t\tfor ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\tahole = holes[ h ];\n\t\t\t\toneHoleMovements = holesMovements[ h ];\n\n\t\t\t\tfor ( i = 0, il = ahole.length; i < il; i ++ ) {\n\n\t\t\t\t\tvert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n\t\t\t\t\tv( vert.x, vert.y, - z );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tbs = bevelSize;\n\n\t\t// Back facing vertices\n\n\t\tfor ( i = 0; i < vlen; i ++ ) {\n\n\t\t\tvert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n\t\t\tif ( ! extrudeByPath ) {\n\n\t\t\t\tv( vert.x, vert.y, 0 );\n\n\t\t\t} else {\n\n\t\t\t\t// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );\n\n\t\t\t\tnormal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );\n\t\t\t\tbinormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );\n\n\t\t\t\tposition2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );\n\n\t\t\t\tv( position2.x, position2.y, position2.z );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Add stepped vertices...\n\t\t// Including front facing vertices\n\n\t\tvar s;\n\n\t\tfor ( s = 1; s <= steps; s ++ ) {\n\n\t\t\tfor ( i = 0; i < vlen; i ++ ) {\n\n\t\t\t\tvert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n\t\t\t\tif ( ! extrudeByPath ) {\n\n\t\t\t\t\tv( vert.x, vert.y, amount / steps * s );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );\n\n\t\t\t\t\tnormal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );\n\t\t\t\t\tbinormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );\n\n\t\t\t\t\tposition2.copy( extrudePts[ s ] ).add( normal ).add( binormal );\n\n\t\t\t\t\tv( position2.x, position2.y, position2.z );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\n\t\t// Add bevel segments planes\n\n\t\t//for ( b = 1; b <= bevelSegments; b ++ ) {\n\t\tfor ( b = bevelSegments - 1; b >= 0; b -- ) {\n\n\t\t\tt = b / bevelSegments;\n\t\t\tz = bevelThickness * Math.cos ( t * Math.PI / 2 );\n\t\t\tbs = bevelSize * Math.sin( t * Math.PI / 2 );\n\n\t\t\t// contract shape\n\n\t\t\tfor ( i = 0, il = contour.length; i < il; i ++ ) {\n\n\t\t\t\tvert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n\t\t\t\tv( vert.x, vert.y, amount + z );\n\n\t\t\t}\n\n\t\t\t// expand holes\n\n\t\t\tfor ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\tahole = holes[ h ];\n\t\t\t\toneHoleMovements = holesMovements[ h ];\n\n\t\t\t\tfor ( i = 0, il = ahole.length; i < il; i ++ ) {\n\n\t\t\t\t\tvert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n\t\t\t\t\tif ( ! extrudeByPath ) {\n\n\t\t\t\t\t\tv( vert.x, vert.y, amount + z );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tv( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t/* Faces */\n\n\t\t// Top and bottom faces\n\n\t\tbuildLidFaces();\n\n\t\t// Sides faces\n\n\t\tbuildSideFaces();\n\n\n\t\t///// Internal functions\n\n\t\tfunction buildLidFaces() {\n\n\t\t\tif ( bevelEnabled ) {\n\n\t\t\t\tvar layer = 0; // steps + 1\n\t\t\t\tvar offset = vlen * layer;\n\n\t\t\t\t// Bottom faces\n\n\t\t\t\tfor ( i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\tface = faces[ i ];\n\t\t\t\t\tf3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );\n\n\t\t\t\t}\n\n\t\t\t\tlayer = steps + bevelSegments * 2;\n\t\t\t\toffset = vlen * layer;\n\n\t\t\t\t// Top faces\n\n\t\t\t\tfor ( i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\tface = faces[ i ];\n\t\t\t\t\tf3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// Bottom faces\n\n\t\t\t\tfor ( i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\tface = faces[ i ];\n\t\t\t\t\tf3( face[ 2 ], face[ 1 ], face[ 0 ] );\n\n\t\t\t\t}\n\n\t\t\t\t// Top faces\n\n\t\t\t\tfor ( i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\tface = faces[ i ];\n\t\t\t\t\tf3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Create faces for the z-sides of the shape\n\n\t\tfunction buildSideFaces() {\n\n\t\t\tvar layeroffset = 0;\n\t\t\tsidewalls( contour, layeroffset );\n\t\t\tlayeroffset += contour.length;\n\n\t\t\tfor ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\tahole = holes[ h ];\n\t\t\t\tsidewalls( ahole, layeroffset );\n\n\t\t\t\t//, true\n\t\t\t\tlayeroffset += ahole.length;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction sidewalls( contour, layeroffset ) {\n\n\t\t\tvar j, k;\n\t\t\ti = contour.length;\n\n\t\t\twhile ( -- i >= 0 ) {\n\n\t\t\t\tj = i;\n\t\t\t\tk = i - 1;\n\t\t\t\tif ( k < 0 ) k = contour.length - 1;\n\n\t\t\t\t//console.log('b', i,j, i-1, k,vertices.length);\n\n\t\t\t\tvar s = 0, sl = steps + bevelSegments * 2;\n\n\t\t\t\tfor ( s = 0; s < sl; s ++ ) {\n\n\t\t\t\t\tvar slen1 = vlen * s;\n\t\t\t\t\tvar slen2 = vlen * ( s + 1 );\n\n\t\t\t\t\tvar a = layeroffset + j + slen1,\n\t\t\t\t\t\tb = layeroffset + k + slen1,\n\t\t\t\t\t\tc = layeroffset + k + slen2,\n\t\t\t\t\t\td = layeroffset + j + slen2;\n\n\t\t\t\t\tf4( a, b, c, d, contour, s, sl, j, k );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\n\t\tfunction v( x, y, z ) {\n\n\t\t\tscope.vertices.push( new Vector3( x, y, z ) );\n\n\t\t}\n\n\t\tfunction f3( a, b, c ) {\n\n\t\t\ta += shapesOffset;\n\t\t\tb += shapesOffset;\n\t\t\tc += shapesOffset;\n\n\t\t\tscope.faces.push( new Face3( a, b, c, null, null, 0 ) );\n\n\t\t\tvar uvs = uvgen.generateTopUV( scope, a, b, c );\n\n\t\t\tscope.faceVertexUvs[ 0 ].push( uvs );\n\n\t\t}\n\n\t\tfunction f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {\n\n\t\t\ta += shapesOffset;\n\t\t\tb += shapesOffset;\n\t\t\tc += shapesOffset;\n\t\t\td += shapesOffset;\n\n\t\t\tscope.faces.push( new Face3( a, b, d, null, null, 1 ) );\n\t\t\tscope.faces.push( new Face3( b, c, d, null, null, 1 ) );\n\n\t\t\tvar uvs = uvgen.generateSideWallUV( scope, a, b, c, d );\n\n\t\t\tscope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );\n\t\t\tscope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );\n\n\t\t}\n\n\t};\n\n\tExtrudeGeometry.WorldUVGenerator = {\n\n\t\tgenerateTopUV: function ( geometry, indexA, indexB, indexC ) {\n\n\t\t\tvar vertices = geometry.vertices;\n\n\t\t\tvar a = vertices[ indexA ];\n\t\t\tvar b = vertices[ indexB ];\n\t\t\tvar c = vertices[ indexC ];\n\n\t\t\treturn [\n\t\t\t\tnew Vector2( a.x, a.y ),\n\t\t\t\tnew Vector2( b.x, b.y ),\n\t\t\t\tnew Vector2( c.x, c.y )\n\t\t\t];\n\n\t\t},\n\n\t\tgenerateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {\n\n\t\t\tvar vertices = geometry.vertices;\n\n\t\t\tvar a = vertices[ indexA ];\n\t\t\tvar b = vertices[ indexB ];\n\t\t\tvar c = vertices[ indexC ];\n\t\t\tvar d = vertices[ indexD ];\n\n\t\t\tif ( Math.abs( a.y - b.y ) < 0.01 ) {\n\n\t\t\t\treturn [\n\t\t\t\t\tnew Vector2( a.x, 1 - a.z ),\n\t\t\t\t\tnew Vector2( b.x, 1 - b.z ),\n\t\t\t\t\tnew Vector2( c.x, 1 - c.z ),\n\t\t\t\t\tnew Vector2( d.x, 1 - d.z )\n\t\t\t\t];\n\n\t\t\t} else {\n\n\t\t\t\treturn [\n\t\t\t\t\tnew Vector2( a.y, 1 - a.z ),\n\t\t\t\t\tnew Vector2( b.y, 1 - b.z ),\n\t\t\t\t\tnew Vector2( c.y, 1 - c.z ),\n\t\t\t\t\tnew Vector2( d.y, 1 - d.z )\n\t\t\t\t];\n\n\t\t\t}\n\n\t\t}\n\t};\n\n\t/**\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * Text = 3D Text\n\t *\n\t * parameters = {\n\t * font: <THREE.Font>, // font\n\t *\n\t * size: <float>, // size of the text\n\t * height: <float>, // thickness to extrude text\n\t * curveSegments: <int>, // number of points on the curves\n\t *\n\t * bevelEnabled: <bool>, // turn on bevel\n\t * bevelThickness: <float>, // how deep into text bevel goes\n\t * bevelSize: <float> // how far from text outline is bevel\n\t * }\n\t */\n\n\tfunction TextGeometry( text, parameters ) {\n\n\t\tparameters = parameters || {};\n\n\t\tvar font = parameters.font;\n\n\t\tif ( (font && font.isFont) === false ) {\n\n\t\t\tconsole.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );\n\t\t\treturn new Geometry();\n\n\t\t}\n\n\t\tvar shapes = font.generateShapes( text, parameters.size, parameters.curveSegments );\n\n\t\t// translate parameters to ExtrudeGeometry API\n\n\t\tparameters.amount = parameters.height !== undefined ? parameters.height : 50;\n\n\t\t// defaults\n\n\t\tif ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;\n\t\tif ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;\n\t\tif ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;\n\n\t\tExtrudeGeometry.call( this, shapes, parameters );\n\n\t\tthis.type = 'TextGeometry';\n\n\t}\n\n\tTextGeometry.prototype = Object.create( ExtrudeGeometry.prototype );\n\tTextGeometry.prototype.constructor = TextGeometry;\n\n\t/**\n\t * @author benaadams / https://twitter.com/ben_a_adams\n\t * based on THREE.SphereGeometry\n\t */\n\n\tfunction SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'SphereBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\tphiStart: phiStart,\n\t\t\tphiLength: phiLength,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tradius = radius || 50;\n\n\t\twidthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );\n\t\theightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );\n\n\t\tphiStart = phiStart !== undefined ? phiStart : 0;\n\t\tphiLength = phiLength !== undefined ? phiLength : Math.PI * 2;\n\n\t\tthetaStart = thetaStart !== undefined ? thetaStart : 0;\n\t\tthetaLength = thetaLength !== undefined ? thetaLength : Math.PI;\n\n\t\tvar thetaEnd = thetaStart + thetaLength;\n\n\t\tvar vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) );\n\n\t\tvar positions = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );\n\n\t\tvar index = 0, vertices = [], normal = new Vector3();\n\n\t\tfor ( var y = 0; y <= heightSegments; y ++ ) {\n\n\t\t\tvar verticesRow = [];\n\n\t\t\tvar v = y / heightSegments;\n\n\t\t\tfor ( var x = 0; x <= widthSegments; x ++ ) {\n\n\t\t\t\tvar u = x / widthSegments;\n\n\t\t\t\tvar px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n\t\t\t\tvar py = radius * Math.cos( thetaStart + v * thetaLength );\n\t\t\t\tvar pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n\n\t\t\t\tnormal.set( px, py, pz ).normalize();\n\n\t\t\t\tpositions.setXYZ( index, px, py, pz );\n\t\t\t\tnormals.setXYZ( index, normal.x, normal.y, normal.z );\n\t\t\t\tuvs.setXY( index, u, 1 - v );\n\n\t\t\t\tverticesRow.push( index );\n\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t\tvertices.push( verticesRow );\n\n\t\t}\n\n\t\tvar indices = [];\n\n\t\tfor ( var y = 0; y < heightSegments; y ++ ) {\n\n\t\t\tfor ( var x = 0; x < widthSegments; x ++ ) {\n\n\t\t\t\tvar v1 = vertices[ y ][ x + 1 ];\n\t\t\t\tvar v2 = vertices[ y ][ x ];\n\t\t\t\tvar v3 = vertices[ y + 1 ][ x ];\n\t\t\t\tvar v4 = vertices[ y + 1 ][ x + 1 ];\n\n\t\t\t\tif ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 );\n\t\t\t\tif ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.setIndex( new ( positions.count > 65535 ? Uint32Attribute : Uint16Attribute )( indices, 1 ) );\n\t\tthis.addAttribute( 'position', positions );\n\t\tthis.addAttribute( 'normal', normals );\n\t\tthis.addAttribute( 'uv', uvs );\n\n\t\tthis.boundingSphere = new Sphere( new Vector3(), radius );\n\n\t}\n\n\tSphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tSphereBufferGeometry.prototype.constructor = SphereBufferGeometry;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'SphereGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\tphiStart: phiStart,\n\t\t\tphiLength: phiLength,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tthis.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );\n\n\t}\n\n\tSphereGeometry.prototype = Object.create( Geometry.prototype );\n\tSphereGeometry.prototype.constructor = SphereGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'RingBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tinnerRadius: innerRadius,\n\t\t\touterRadius: outerRadius,\n\t\t\tthetaSegments: thetaSegments,\n\t\t\tphiSegments: phiSegments,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tinnerRadius = innerRadius || 20;\n\t\touterRadius = outerRadius || 50;\n\n\t\tthetaStart = thetaStart !== undefined ? thetaStart : 0;\n\t\tthetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n\t\tthetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;\n\t\tphiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1;\n\n\t\t// these are used to calculate buffer length\n\t\tvar vertexCount = ( thetaSegments + 1 ) * ( phiSegments + 1 );\n\t\tvar indexCount = thetaSegments * phiSegments * 2 * 3;\n\n\t\t// buffers\n\t\tvar indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 );\n\t\tvar vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );\n\n\t\t// some helper variables\n\t\tvar index = 0, indexOffset = 0, segment;\n\t\tvar radius = innerRadius;\n\t\tvar radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );\n\t\tvar vertex = new Vector3();\n\t\tvar uv = new Vector2();\n\t\tvar j, i;\n\n\t\t// generate vertices, normals and uvs\n\n\t\t// values are generate from the inside of the ring to the outside\n\n\t\tfor ( j = 0; j <= phiSegments; j ++ ) {\n\n\t\t\tfor ( i = 0; i <= thetaSegments; i ++ ) {\n\n\t\t\t\tsegment = thetaStart + i / thetaSegments * thetaLength;\n\n\t\t\t\t// vertex\n\t\t\t\tvertex.x = radius * Math.cos( segment );\n\t\t\t\tvertex.y = radius * Math.sin( segment );\n\t\t\t\tvertices.setXYZ( index, vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\t\t\t\tnormals.setXYZ( index, 0, 0, 1 );\n\n\t\t\t\t// uv\n\t\t\t\tuv.x = ( vertex.x / outerRadius + 1 ) / 2;\n\t\t\t\tuv.y = ( vertex.y / outerRadius + 1 ) / 2;\n\t\t\t\tuvs.setXY( index, uv.x, uv.y );\n\n\t\t\t\t// increase index\n\t\t\t\tindex++;\n\n\t\t\t}\n\n\t\t\t// increase the radius for next row of vertices\n\t\t\tradius += radiusStep;\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tfor ( j = 0; j < phiSegments; j ++ ) {\n\n\t\t\tvar thetaSegmentLevel = j * ( thetaSegments + 1 );\n\n\t\t\tfor ( i = 0; i < thetaSegments; i ++ ) {\n\n\t\t\t\tsegment = i + thetaSegmentLevel;\n\n\t\t\t\t// indices\n\t\t\t\tvar a = segment;\n\t\t\t\tvar b = segment + thetaSegments + 1;\n\t\t\t\tvar c = segment + thetaSegments + 2;\n\t\t\t\tvar d = segment + 1;\n\n\t\t\t\t// face one\n\t\t\t\tindices.setX( indexOffset, a ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, b ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, c ); indexOffset++;\n\n\t\t\t\t// face two\n\t\t\t\tindices.setX( indexOffset, a ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, c ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, d ); indexOffset++;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.addAttribute( 'position', vertices );\n\t\tthis.addAttribute( 'normal', normals );\n\t\tthis.addAttribute( 'uv', uvs );\n\n\t}\n\n\tRingBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tRingBufferGeometry.prototype.constructor = RingBufferGeometry;\n\n\t/**\n\t * @author Kaleb Murphy\n\t */\n\n\tfunction RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'RingGeometry';\n\n\t\tthis.parameters = {\n\t\t\tinnerRadius: innerRadius,\n\t\t\touterRadius: outerRadius,\n\t\t\tthetaSegments: thetaSegments,\n\t\t\tphiSegments: phiSegments,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tthis.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) );\n\n\t}\n\n\tRingGeometry.prototype = Object.create( Geometry.prototype );\n\tRingGeometry.prototype.constructor = RingGeometry;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as\n\t */\n\n\tfunction PlaneGeometry( width, height, widthSegments, heightSegments ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'PlaneGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments\n\t\t};\n\n\t\tthis.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );\n\n\t}\n\n\tPlaneGeometry.prototype = Object.create( Geometry.prototype );\n\tPlaneGeometry.prototype.constructor = PlaneGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\t // points - to create a closed torus, one must use a set of points\n\t // like so: [ a, b, c, d, a ], see first is the same as last.\n\t // segments - the number of circumference segments to create\n\t // phiStart - the starting radian\n\t // phiLength - the radian (0 to 2PI) range of the lathed section\n\t // 2PI is a closed lathe, less than 2PI is a portion.\n\n\tfunction LatheBufferGeometry( points, segments, phiStart, phiLength ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'LatheBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tpoints: points,\n\t\t\tsegments: segments,\n\t\t\tphiStart: phiStart,\n\t\t\tphiLength: phiLength\n\t\t};\n\n\t\tsegments = Math.floor( segments ) || 12;\n\t\tphiStart = phiStart || 0;\n\t\tphiLength = phiLength || Math.PI * 2;\n\n\t\t// clamp phiLength so it's in range of [ 0, 2PI ]\n\t\tphiLength = _Math.clamp( phiLength, 0, Math.PI * 2 );\n\n\t\t// these are used to calculate buffer length\n\t\tvar vertexCount = ( segments + 1 ) * points.length;\n\t\tvar indexCount = segments * points.length * 2 * 3;\n\n\t\t// buffers\n\t\tvar indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 );\n\t\tvar vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );\n\n\t\t// helper variables\n\t\tvar index = 0, indexOffset = 0, base;\n\t\tvar inverseSegments = 1.0 / segments;\n\t\tvar vertex = new Vector3();\n\t\tvar uv = new Vector2();\n\t\tvar i, j;\n\n\t\t// generate vertices and uvs\n\n\t\tfor ( i = 0; i <= segments; i ++ ) {\n\n\t\t\tvar phi = phiStart + i * inverseSegments * phiLength;\n\n\t\t\tvar sin = Math.sin( phi );\n\t\t\tvar cos = Math.cos( phi );\n\n\t\t\tfor ( j = 0; j <= ( points.length - 1 ); j ++ ) {\n\n\t\t\t\t// vertex\n\t\t\t\tvertex.x = points[ j ].x * sin;\n\t\t\t\tvertex.y = points[ j ].y;\n\t\t\t\tvertex.z = points[ j ].x * cos;\n\t\t\t\tvertices.setXYZ( index, vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// uv\n\t\t\t\tuv.x = i / segments;\n\t\t\t\tuv.y = j / ( points.length - 1 );\n\t\t\t\tuvs.setXY( index, uv.x, uv.y );\n\n\t\t\t\t// increase index\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tfor ( i = 0; i < segments; i ++ ) {\n\n\t\t\tfor ( j = 0; j < ( points.length - 1 ); j ++ ) {\n\n\t\t\t\tbase = j + i * points.length;\n\n\t\t\t\t// indices\n\t\t\t\tvar a = base;\n\t\t\t\tvar b = base + points.length;\n\t\t\t\tvar c = base + points.length + 1;\n\t\t\t\tvar d = base + 1;\n\n\t\t\t\t// face one\n\t\t\t\tindices.setX( indexOffset, a ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, b ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, d ); indexOffset++;\n\n\t\t\t\t// face two\n\t\t\t\tindices.setX( indexOffset, b ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, c ); indexOffset++;\n\t\t\t\tindices.setX( indexOffset, d ); indexOffset++;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.addAttribute( 'position', vertices );\n\t\tthis.addAttribute( 'uv', uvs );\n\n\t\t// generate normals\n\n\t\tthis.computeVertexNormals();\n\n\t\t// if the geometry is closed, we need to average the normals along the seam.\n\t\t// because the corresponding vertices are identical (but still have different UVs).\n\n\t\tif( phiLength === Math.PI * 2 ) {\n\n\t\t\tvar normals = this.attributes.normal.array;\n\t\t\tvar n1 = new Vector3();\n\t\t\tvar n2 = new Vector3();\n\t\t\tvar n = new Vector3();\n\n\t\t\t// this is the buffer offset for the last line of vertices\n\t\t\tbase = segments * points.length * 3;\n\n\t\t\tfor( i = 0, j = 0; i < points.length; i ++, j += 3 ) {\n\n\t\t\t\t// select the normal of the vertex in the first line\n\t\t\t\tn1.x = normals[ j + 0 ];\n\t\t\t\tn1.y = normals[ j + 1 ];\n\t\t\t\tn1.z = normals[ j + 2 ];\n\n\t\t\t\t// select the normal of the vertex in the last line\n\t\t\t\tn2.x = normals[ base + j + 0 ];\n\t\t\t\tn2.y = normals[ base + j + 1 ];\n\t\t\t\tn2.z = normals[ base + j + 2 ];\n\n\t\t\t\t// average normals\n\t\t\t\tn.addVectors( n1, n2 ).normalize();\n\n\t\t\t\t// assign the new values to both normals\n\t\t\t\tnormals[ j + 0 ] = normals[ base + j + 0 ] = n.x;\n\t\t\t\tnormals[ j + 1 ] = normals[ base + j + 1 ] = n.y;\n\t\t\t\tnormals[ j + 2 ] = normals[ base + j + 2 ] = n.z;\n\n\t\t\t} // next row\n\n\t\t}\n\n\t}\n\n\tLatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tLatheBufferGeometry.prototype.constructor = LatheBufferGeometry;\n\n\t/**\n\t * @author astrodud / http://astrodud.isgreat.org/\n\t * @author zz85 / https://github.com/zz85\n\t * @author bhouston / http://clara.io\n\t */\n\n\t// points - to create a closed torus, one must use a set of points\n\t// like so: [ a, b, c, d, a ], see first is the same as last.\n\t// segments - the number of circumference segments to create\n\t// phiStart - the starting radian\n\t// phiLength - the radian (0 to 2PI) range of the lathed section\n\t// 2PI is a closed lathe, less than 2PI is a portion.\n\n\tfunction LatheGeometry( points, segments, phiStart, phiLength ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'LatheGeometry';\n\n\t\tthis.parameters = {\n\t\t\tpoints: points,\n\t\t\tsegments: segments,\n\t\t\tphiStart: phiStart,\n\t\t\tphiLength: phiLength\n\t\t};\n\n\t\tthis.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tLatheGeometry.prototype = Object.create( Geometry.prototype );\n\tLatheGeometry.prototype.constructor = LatheGeometry;\n\n\t/**\n\t * @author jonobr1 / http://jonobr1.com\n\t *\n\t * Creates a one-sided polygonal geometry from a path shape. Similar to\n\t * ExtrudeGeometry.\n\t *\n\t * parameters = {\n\t *\n\t *\tcurveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT.\n\t *\n\t *\tmaterial: <int> // material index for front and back faces\n\t *\tuvGenerator: <Object> // object that provides UV generator functions\n\t *\n\t * }\n\t **/\n\n\tfunction ShapeGeometry( shapes, options ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'ShapeGeometry';\n\n\t\tif ( Array.isArray( shapes ) === false ) shapes = [ shapes ];\n\n\t\tthis.addShapeList( shapes, options );\n\n\t\tthis.computeFaceNormals();\n\n\t}\n\n\tShapeGeometry.prototype = Object.create( Geometry.prototype );\n\tShapeGeometry.prototype.constructor = ShapeGeometry;\n\n\t/**\n\t * Add an array of shapes to THREE.ShapeGeometry.\n\t */\n\tShapeGeometry.prototype.addShapeList = function ( shapes, options ) {\n\n\t\tfor ( var i = 0, l = shapes.length; i < l; i ++ ) {\n\n\t\t\tthis.addShape( shapes[ i ], options );\n\n\t\t}\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.\n\t */\n\tShapeGeometry.prototype.addShape = function ( shape, options ) {\n\n\t\tif ( options === undefined ) options = {};\n\t\tvar curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;\n\n\t\tvar material = options.material;\n\t\tvar uvgen = options.UVGenerator === undefined ? ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;\n\n\t\t//\n\n\t\tvar i, l, hole;\n\n\t\tvar shapesOffset = this.vertices.length;\n\t\tvar shapePoints = shape.extractPoints( curveSegments );\n\n\t\tvar vertices = shapePoints.shape;\n\t\tvar holes = shapePoints.holes;\n\n\t\tvar reverse = ! ShapeUtils.isClockWise( vertices );\n\n\t\tif ( reverse ) {\n\n\t\t\tvertices = vertices.reverse();\n\n\t\t\t// Maybe we should also check if holes are in the opposite direction, just to be safe...\n\n\t\t\tfor ( i = 0, l = holes.length; i < l; i ++ ) {\n\n\t\t\t\thole = holes[ i ];\n\n\t\t\t\tif ( ShapeUtils.isClockWise( hole ) ) {\n\n\t\t\t\t\tholes[ i ] = hole.reverse();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treverse = false;\n\n\t\t}\n\n\t\tvar faces = ShapeUtils.triangulateShape( vertices, holes );\n\n\t\t// Vertices\n\n\t\tfor ( i = 0, l = holes.length; i < l; i ++ ) {\n\n\t\t\thole = holes[ i ];\n\t\t\tvertices = vertices.concat( hole );\n\n\t\t}\n\n\t\t//\n\n\t\tvar vert, vlen = vertices.length;\n\t\tvar face, flen = faces.length;\n\n\t\tfor ( i = 0; i < vlen; i ++ ) {\n\n\t\t\tvert = vertices[ i ];\n\n\t\t\tthis.vertices.push( new Vector3( vert.x, vert.y, 0 ) );\n\n\t\t}\n\n\t\tfor ( i = 0; i < flen; i ++ ) {\n\n\t\t\tface = faces[ i ];\n\n\t\t\tvar a = face[ 0 ] + shapesOffset;\n\t\t\tvar b = face[ 1 ] + shapesOffset;\n\t\t\tvar c = face[ 2 ] + shapesOffset;\n\n\t\t\tthis.faces.push( new Face3( a, b, c, null, null, material ) );\n\t\t\tthis.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author WestLangley / http://github.com/WestLangley\n\t */\n\n\tfunction EdgesGeometry( geometry, thresholdAngle ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;\n\n\t\tvar thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle );\n\n\t\tvar edge = [ 0, 0 ], hash = {};\n\n\t\tfunction sortFunction( a, b ) {\n\n\t\t\treturn a - b;\n\n\t\t}\n\n\t\tvar keys = [ 'a', 'b', 'c' ];\n\n\t\tvar geometry2;\n\n\t\tif ( (geometry && geometry.isBufferGeometry) ) {\n\n\t\t\tgeometry2 = new Geometry();\n\t\t\tgeometry2.fromBufferGeometry( geometry );\n\n\t\t} else {\n\n\t\t\tgeometry2 = geometry.clone();\n\n\t\t}\n\n\t\tgeometry2.mergeVertices();\n\t\tgeometry2.computeFaceNormals();\n\n\t\tvar vertices = geometry2.vertices;\n\t\tvar faces = geometry2.faces;\n\n\t\tfor ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n\t\t\tvar face = faces[ i ];\n\n\t\t\tfor ( var j = 0; j < 3; j ++ ) {\n\n\t\t\t\tedge[ 0 ] = face[ keys[ j ] ];\n\t\t\t\tedge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];\n\t\t\t\tedge.sort( sortFunction );\n\n\t\t\t\tvar key = edge.toString();\n\n\t\t\t\tif ( hash[ key ] === undefined ) {\n\n\t\t\t\t\thash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };\n\n\t\t\t\t} else {\n\n\t\t\t\t\thash[ key ].face2 = i;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tvar coords = [];\n\n\t\tfor ( var key in hash ) {\n\n\t\t\tvar h = hash[ key ];\n\n\t\t\tif ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) {\n\n\t\t\t\tvar vertex = vertices[ h.vert1 ];\n\t\t\t\tcoords.push( vertex.x );\n\t\t\t\tcoords.push( vertex.y );\n\t\t\t\tcoords.push( vertex.z );\n\n\t\t\t\tvertex = vertices[ h.vert2 ];\n\t\t\t\tcoords.push( vertex.x );\n\t\t\t\tcoords.push( vertex.y );\n\t\t\t\tcoords.push( vertex.z );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.addAttribute( 'position', new BufferAttribute( new Float32Array( coords ), 3 ) );\n\n\t}\n\n\tEdgesGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tEdgesGeometry.prototype.constructor = EdgesGeometry;\n\n\t/**\n\t * @author Mugen87 / https://github.com/Mugen87\n\t */\n\n\tfunction CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'CylinderBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradiusTop: radiusTop,\n\t\t\tradiusBottom: radiusBottom,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tvar scope = this;\n\n\t\tradiusTop = radiusTop !== undefined ? radiusTop : 20;\n\t\tradiusBottom = radiusBottom !== undefined ? radiusBottom : 20;\n\t\theight = height !== undefined ? height : 100;\n\n\t\tradialSegments = Math.floor( radialSegments ) || 8;\n\t\theightSegments = Math.floor( heightSegments ) || 1;\n\n\t\topenEnded = openEnded !== undefined ? openEnded : false;\n\t\tthetaStart = thetaStart !== undefined ? thetaStart : 0.0;\n\t\tthetaLength = thetaLength !== undefined ? thetaLength : 2.0 * Math.PI;\n\n\t\t// used to calculate buffer length\n\n\t\tvar nbCap = 0;\n\n\t\tif ( openEnded === false ) {\n\n\t\t\tif ( radiusTop > 0 ) nbCap ++;\n\t\t\tif ( radiusBottom > 0 ) nbCap ++;\n\n\t\t}\n\n\t\tvar vertexCount = calculateVertexCount();\n\t\tvar indexCount = calculateIndexCount();\n\n\t\t// buffers\n\n\t\tvar indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ), 1 );\n\t\tvar vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );\n\t\tvar uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );\n\n\t\t// helper variables\n\n\t\tvar index = 0,\n\t\t indexOffset = 0,\n\t\t indexArray = [],\n\t\t halfHeight = height / 2;\n\n\t\t// group variables\n\t\tvar groupStart = 0;\n\n\t\t// generate geometry\n\n\t\tgenerateTorso();\n\n\t\tif ( openEnded === false ) {\n\n\t\t\tif ( radiusTop > 0 ) generateCap( true );\n\t\t\tif ( radiusBottom > 0 ) generateCap( false );\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.addAttribute( 'position', vertices );\n\t\tthis.addAttribute( 'normal', normals );\n\t\tthis.addAttribute( 'uv', uvs );\n\n\t\t// helper functions\n\n\t\tfunction calculateVertexCount() {\n\n\t\t\tvar count = ( radialSegments + 1 ) * ( heightSegments + 1 );\n\n\t\t\tif ( openEnded === false ) {\n\n\t\t\t\tcount += ( ( radialSegments + 1 ) * nbCap ) + ( radialSegments * nbCap );\n\n\t\t\t}\n\n\t\t\treturn count;\n\n\t\t}\n\n\t\tfunction calculateIndexCount() {\n\n\t\t\tvar count = radialSegments * heightSegments * 2 * 3;\n\n\t\t\tif ( openEnded === false ) {\n\n\t\t\t\tcount += radialSegments * nbCap * 3;\n\n\t\t\t}\n\n\t\t\treturn count;\n\n\t\t}\n\n\t\tfunction generateTorso() {\n\n\t\t\tvar x, y;\n\t\t\tvar normal = new Vector3();\n\t\t\tvar vertex = new Vector3();\n\n\t\t\tvar groupCount = 0;\n\n\t\t\t// this will be used to calculate the normal\n\t\t\tvar slope = ( radiusBottom - radiusTop ) / height;\n\n\t\t\t// generate vertices, normals and uvs\n\n\t\t\tfor ( y = 0; y <= heightSegments; y ++ ) {\n\n\t\t\t\tvar indexRow = [];\n\n\t\t\t\tvar v = y / heightSegments;\n\n\t\t\t\t// calculate the radius of the current row\n\t\t\t\tvar radius = v * ( radiusBottom - radiusTop ) + radiusTop;\n\n\t\t\t\tfor ( x = 0; x <= radialSegments; x ++ ) {\n\n\t\t\t\t\tvar u = x / radialSegments;\n\n\t\t\t\t\tvar theta = u * thetaLength + thetaStart;\n\n\t\t\t\t\tvar sinTheta = Math.sin( theta );\n\t\t\t\t\tvar cosTheta = Math.cos( theta );\n\n\t\t\t\t\t// vertex\n\t\t\t\t\tvertex.x = radius * sinTheta;\n\t\t\t\t\tvertex.y = - v * height + halfHeight;\n\t\t\t\t\tvertex.z = radius * cosTheta;\n\t\t\t\t\tvertices.setXYZ( index, vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t\t// normal\n\t\t\t\t\tnormal.set( sinTheta, slope, cosTheta ).normalize();\n\t\t\t\t\tnormals.setXYZ( index, normal.x, normal.y, normal.z );\n\n\t\t\t\t\t// uv\n\t\t\t\t\tuvs.setXY( index, u, 1 - v );\n\n\t\t\t\t\t// save index of vertex in respective row\n\t\t\t\t\tindexRow.push( index );\n\n\t\t\t\t\t// increase index\n\t\t\t\t\tindex ++;\n\n\t\t\t\t}\n\n\t\t\t\t// now save vertices of the row in our index array\n\t\t\t\tindexArray.push( indexRow );\n\n\t\t\t}\n\n\t\t\t// generate indices\n\n\t\t\tfor ( x = 0; x < radialSegments; x ++ ) {\n\n\t\t\t\tfor ( y = 0; y < heightSegments; y ++ ) {\n\n\t\t\t\t\t// we use the index array to access the correct indices\n\t\t\t\t\tvar i1 = indexArray[ y ][ x ];\n\t\t\t\t\tvar i2 = indexArray[ y + 1 ][ x ];\n\t\t\t\t\tvar i3 = indexArray[ y + 1 ][ x + 1 ];\n\t\t\t\t\tvar i4 = indexArray[ y ][ x + 1 ];\n\n\t\t\t\t\t// face one\n\t\t\t\t\tindices.setX( indexOffset, i1 ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, i2 ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, i4 ); indexOffset ++;\n\n\t\t\t\t\t// face two\n\t\t\t\t\tindices.setX( indexOffset, i2 ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, i3 ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, i4 ); indexOffset ++;\n\n\t\t\t\t\t// update counters\n\t\t\t\t\tgroupCount += 6;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\t\t\tscope.addGroup( groupStart, groupCount, 0 );\n\n\t\t\t// calculate new start value for groups\n\t\t\tgroupStart += groupCount;\n\n\t\t}\n\n\t\tfunction generateCap( top ) {\n\n\t\t\tvar x, centerIndexStart, centerIndexEnd;\n\n\t\t\tvar uv = new Vector2();\n\t\t\tvar vertex = new Vector3();\n\n\t\t\tvar groupCount = 0;\n\n\t\t\tvar radius = ( top === true ) ? radiusTop : radiusBottom;\n\t\t\tvar sign = ( top === true ) ? 1 : - 1;\n\n\t\t\t// save the index of the first center vertex\n\t\t\tcenterIndexStart = index;\n\n\t\t\t// first we generate the center vertex data of the cap.\n\t\t\t// because the geometry needs one set of uvs per face,\n\t\t\t// we must generate a center vertex per face/segment\n\n\t\t\tfor ( x = 1; x <= radialSegments; x ++ ) {\n\n\t\t\t\t// vertex\n\t\t\t\tvertices.setXYZ( index, 0, halfHeight * sign, 0 );\n\n\t\t\t\t// normal\n\t\t\t\tnormals.setXYZ( index, 0, sign, 0 );\n\n\t\t\t\t// uv\n\t\t\t\tuv.x = 0.5;\n\t\t\t\tuv.y = 0.5;\n\n\t\t\t\tuvs.setXY( index, uv.x, uv.y );\n\n\t\t\t\t// increase index\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t\t// save the index of the last center vertex\n\t\t\tcenterIndexEnd = index;\n\n\t\t\t// now we generate the surrounding vertices, normals and uvs\n\n\t\t\tfor ( x = 0; x <= radialSegments; x ++ ) {\n\n\t\t\t\tvar u = x / radialSegments;\n\t\t\t\tvar theta = u * thetaLength + thetaStart;\n\n\t\t\t\tvar cosTheta = Math.cos( theta );\n\t\t\t\tvar sinTheta = Math.sin( theta );\n\n\t\t\t\t// vertex\n\t\t\t\tvertex.x = radius * sinTheta;\n\t\t\t\tvertex.y = halfHeight * sign;\n\t\t\t\tvertex.z = radius * cosTheta;\n\t\t\t\tvertices.setXYZ( index, vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\t\t\t\tnormals.setXYZ( index, 0, sign, 0 );\n\n\t\t\t\t// uv\n\t\t\t\tuv.x = ( cosTheta * 0.5 ) + 0.5;\n\t\t\t\tuv.y = ( sinTheta * 0.5 * sign ) + 0.5;\n\t\t\t\tuvs.setXY( index, uv.x, uv.y );\n\n\t\t\t\t// increase index\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t\t// generate indices\n\n\t\t\tfor ( x = 0; x < radialSegments; x ++ ) {\n\n\t\t\t\tvar c = centerIndexStart + x;\n\t\t\t\tvar i = centerIndexEnd + x;\n\n\t\t\t\tif ( top === true ) {\n\n\t\t\t\t\t// face top\n\t\t\t\t\tindices.setX( indexOffset, i ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, i + 1 ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, c ); indexOffset ++;\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// face bottom\n\t\t\t\t\tindices.setX( indexOffset, i + 1 ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, i ); indexOffset ++;\n\t\t\t\t\tindices.setX( indexOffset, c ); indexOffset ++;\n\n\t\t\t\t}\n\n\t\t\t\t// update counters\n\t\t\t\tgroupCount += 3;\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\t\t\tscope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );\n\n\t\t\t// calculate new start value for groups\n\t\t\tgroupStart += groupCount;\n\n\t\t}\n\n\t}\n\n\tCylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tCylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'CylinderGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradiusTop: radiusTop,\n\t\t\tradiusBottom: radiusBottom,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tthis.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tCylinderGeometry.prototype = Object.create( Geometry.prototype );\n\tCylinderGeometry.prototype.constructor = CylinderGeometry;\n\n\t/**\n\t * @author abelnation / http://github.com/abelnation\n\t */\n\n\tfunction ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n\t\tCylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n\t\tthis.type = 'ConeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t}\n\n\tConeGeometry.prototype = Object.create( CylinderGeometry.prototype );\n\tConeGeometry.prototype.constructor = ConeGeometry;\n\n\t/**\n\t * @author: abelnation / http://github.com/abelnation\n\t */\n\n\tfunction ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n\t\tCylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n\t\tthis.type = 'ConeBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t}\n\n\tConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype );\n\tConeBufferGeometry.prototype.constructor = ConeBufferGeometry;\n\n\t/**\n\t * @author benaadams / https://twitter.com/ben_a_adams\n\t */\n\n\tfunction CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) {\n\n\t\tBufferGeometry.call( this );\n\n\t\tthis.type = 'CircleBufferGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tsegments: segments,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tradius = radius || 50;\n\t\tsegments = segments !== undefined ? Math.max( 3, segments ) : 8;\n\n\t\tthetaStart = thetaStart !== undefined ? thetaStart : 0;\n\t\tthetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n\t\tvar vertices = segments + 2;\n\n\t\tvar positions = new Float32Array( vertices * 3 );\n\t\tvar normals = new Float32Array( vertices * 3 );\n\t\tvar uvs = new Float32Array( vertices * 2 );\n\n\t\t// center data is already zero, but need to set a few extras\n\t\tnormals[ 2 ] = 1.0;\n\t\tuvs[ 0 ] = 0.5;\n\t\tuvs[ 1 ] = 0.5;\n\n\t\tfor ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) {\n\n\t\t\tvar segment = thetaStart + s / segments * thetaLength;\n\n\t\t\tpositions[ i ] = radius * Math.cos( segment );\n\t\t\tpositions[ i + 1 ] = radius * Math.sin( segment );\n\n\t\t\tnormals[ i + 2 ] = 1; // normal z\n\n\t\t\tuvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2;\n\t\t\tuvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2;\n\n\t\t}\n\n\t\tvar indices = [];\n\n\t\tfor ( var i = 1; i <= segments; i ++ ) {\n\n\t\t\tindices.push( i, i + 1, 0 );\n\n\t\t}\n\n\t\tthis.setIndex( new BufferAttribute( new Uint16Array( indices ), 1 ) );\n\t\tthis.addAttribute( 'position', new BufferAttribute( positions, 3 ) );\n\t\tthis.addAttribute( 'normal', new BufferAttribute( normals, 3 ) );\n\t\tthis.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) );\n\n\t\tthis.boundingSphere = new Sphere( new Vector3(), radius );\n\n\t}\n\n\tCircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n\tCircleBufferGeometry.prototype.constructor = CircleBufferGeometry;\n\n\t/**\n\t * @author hughes\n\t */\n\n\tfunction CircleGeometry( radius, segments, thetaStart, thetaLength ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'CircleGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tsegments: segments,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tthis.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );\n\n\t}\n\n\tCircleGeometry.prototype = Object.create( Geometry.prototype );\n\tCircleGeometry.prototype.constructor = CircleGeometry;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as\n\t */\n\n\tfunction BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {\n\n\t\tGeometry.call( this );\n\n\t\tthis.type = 'BoxGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\tdepth: depth,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\tdepthSegments: depthSegments\n\t\t};\n\n\t\tthis.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) );\n\t\tthis.mergeVertices();\n\n\t}\n\n\tBoxGeometry.prototype = Object.create( Geometry.prototype );\n\tBoxGeometry.prototype.constructor = BoxGeometry;\n\n\n\n\tvar Geometries = Object.freeze({\n\t\tWireframeGeometry: WireframeGeometry,\n\t\tParametricGeometry: ParametricGeometry,\n\t\tParametricBufferGeometry: ParametricBufferGeometry,\n\t\tTetrahedronGeometry: TetrahedronGeometry,\n\t\tTetrahedronBufferGeometry: TetrahedronBufferGeometry,\n\t\tOctahedronGeometry: OctahedronGeometry,\n\t\tOctahedronBufferGeometry: OctahedronBufferGeometry,\n\t\tIcosahedronGeometry: IcosahedronGeometry,\n\t\tIcosahedronBufferGeometry: IcosahedronBufferGeometry,\n\t\tDodecahedronGeometry: DodecahedronGeometry,\n\t\tDodecahedronBufferGeometry: DodecahedronBufferGeometry,\n\t\tPolyhedronGeometry: PolyhedronGeometry,\n\t\tPolyhedronBufferGeometry: PolyhedronBufferGeometry,\n\t\tTubeGeometry: TubeGeometry,\n\t\tTubeBufferGeometry: TubeBufferGeometry,\n\t\tTorusKnotGeometry: TorusKnotGeometry,\n\t\tTorusKnotBufferGeometry: TorusKnotBufferGeometry,\n\t\tTorusGeometry: TorusGeometry,\n\t\tTorusBufferGeometry: TorusBufferGeometry,\n\t\tTextGeometry: TextGeometry,\n\t\tSphereBufferGeometry: SphereBufferGeometry,\n\t\tSphereGeometry: SphereGeometry,\n\t\tRingGeometry: RingGeometry,\n\t\tRingBufferGeometry: RingBufferGeometry,\n\t\tPlaneBufferGeometry: PlaneBufferGeometry,\n\t\tPlaneGeometry: PlaneGeometry,\n\t\tLatheGeometry: LatheGeometry,\n\t\tLatheBufferGeometry: LatheBufferGeometry,\n\t\tShapeGeometry: ShapeGeometry,\n\t\tExtrudeGeometry: ExtrudeGeometry,\n\t\tEdgesGeometry: EdgesGeometry,\n\t\tConeGeometry: ConeGeometry,\n\t\tConeBufferGeometry: ConeBufferGeometry,\n\t\tCylinderGeometry: CylinderGeometry,\n\t\tCylinderBufferGeometry: CylinderBufferGeometry,\n\t\tCircleBufferGeometry: CircleBufferGeometry,\n\t\tCircleGeometry: CircleGeometry,\n\t\tBoxBufferGeometry: BoxBufferGeometry,\n\t\tBoxGeometry: BoxGeometry\n\t});\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction ShadowMaterial() {\n\n\t\tShaderMaterial.call( this, {\n\t\t\tuniforms: UniformsUtils.merge( [\n\t\t\t\tUniformsLib[ \"lights\" ],\n\t\t\t\t{\n\t\t\t\t\topacity: { value: 1.0 }\n\t\t\t\t}\n\t\t\t] ),\n\t\t\tvertexShader: ShaderChunk[ 'shadow_vert' ],\n\t\t\tfragmentShader: ShaderChunk[ 'shadow_frag' ]\n\t\t} );\n\n\t\tthis.lights = true;\n\t\tthis.transparent = true;\n\n\t\tObject.defineProperties( this, {\n\t\t\topacity: {\n\t\t\t\tenumerable: true,\n\t\t\t\tget: function () {\n\t\t\t\t\treturn this.uniforms.opacity.value;\n\t\t\t\t},\n\t\t\t\tset: function ( value ) {\n\t\t\t\t\tthis.uniforms.opacity.value = value;\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\n\t}\n\n\tShadowMaterial.prototype = Object.create( ShaderMaterial.prototype );\n\tShadowMaterial.prototype.constructor = ShadowMaterial;\n\n\tShadowMaterial.prototype.isShadowMaterial = true;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction RawShaderMaterial( parameters ) {\n\n\t\tShaderMaterial.call( this, parameters );\n\n\t\tthis.type = 'RawShaderMaterial';\n\n\t}\n\n\tRawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype );\n\tRawShaderMaterial.prototype.constructor = RawShaderMaterial;\n\n\tRawShaderMaterial.prototype.isRawShaderMaterial = true;\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction MultiMaterial( materials ) {\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\tthis.type = 'MultiMaterial';\n\n\t\tthis.materials = materials instanceof Array ? materials : [];\n\n\t\tthis.visible = true;\n\n\t}\n\n\tMultiMaterial.prototype = {\n\n\t\tconstructor: MultiMaterial,\n\n\t\tisMultiMaterial: true,\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\tvar output = {\n\t\t\t\tmetadata: {\n\t\t\t\t\tversion: 4.2,\n\t\t\t\t\ttype: 'material',\n\t\t\t\t\tgenerator: 'MaterialExporter'\n\t\t\t\t},\n\t\t\t\tuuid: this.uuid,\n\t\t\t\ttype: this.type,\n\t\t\t\tmaterials: []\n\t\t\t};\n\n\t\t\tvar materials = this.materials;\n\n\t\t\tfor ( var i = 0, l = materials.length; i < l; i ++ ) {\n\n\t\t\t\tvar material = materials[ i ].toJSON( meta );\n\t\t\t\tdelete material.metadata;\n\n\t\t\t\toutput.materials.push( material );\n\n\t\t\t}\n\n\t\t\toutput.visible = this.visible;\n\n\t\t\treturn output;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\tvar material = new this.constructor();\n\n\t\t\tfor ( var i = 0; i < this.materials.length; i ++ ) {\n\n\t\t\t\tmaterial.materials.push( this.materials[ i ].clone() );\n\n\t\t\t}\n\n\t\t\tmaterial.visible = this.visible;\n\n\t\t\treturn material;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author WestLangley / http://github.com/WestLangley\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * roughness: <float>,\n\t * metalness: <float>,\n\t * opacity: <float>,\n\t *\n\t * map: new THREE.Texture( <Image> ),\n\t *\n\t * lightMap: new THREE.Texture( <Image> ),\n\t * lightMapIntensity: <float>\n\t *\n\t * aoMap: new THREE.Texture( <Image> ),\n\t * aoMapIntensity: <float>\n\t *\n\t * emissive: <hex>,\n\t * emissiveIntensity: <float>\n\t * emissiveMap: new THREE.Texture( <Image> ),\n\t *\n\t * bumpMap: new THREE.Texture( <Image> ),\n\t * bumpScale: <float>,\n\t *\n\t * normalMap: new THREE.Texture( <Image> ),\n\t * normalScale: <Vector2>,\n\t *\n\t * displacementMap: new THREE.Texture( <Image> ),\n\t * displacementScale: <float>,\n\t * displacementBias: <float>,\n\t *\n\t * roughnessMap: new THREE.Texture( <Image> ),\n\t *\n\t * metalnessMap: new THREE.Texture( <Image> ),\n\t *\n\t * alphaMap: new THREE.Texture( <Image> ),\n\t *\n\t * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n\t * envMapIntensity: <float>\n\t *\n\t * refractionRatio: <float>,\n\t *\n\t * wireframe: <boolean>,\n\t * wireframeLinewidth: <float>,\n\t *\n\t * skinning: <bool>,\n\t * morphTargets: <bool>,\n\t * morphNormals: <bool>\n\t * }\n\t */\n\n\tfunction MeshStandardMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.defines = { 'STANDARD': '' };\n\n\t\tthis.type = 'MeshStandardMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // diffuse\n\t\tthis.roughness = 0.5;\n\t\tthis.metalness = 0.5;\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.emissive = new Color( 0x000000 );\n\t\tthis.emissiveIntensity = 1.0;\n\t\tthis.emissiveMap = null;\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.roughnessMap = null;\n\n\t\tthis.metalnessMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.envMapIntensity = 1.0;\n\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.skinning = false;\n\t\tthis.morphTargets = false;\n\t\tthis.morphNormals = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tMeshStandardMaterial.prototype = Object.create( Material.prototype );\n\tMeshStandardMaterial.prototype.constructor = MeshStandardMaterial;\n\n\tMeshStandardMaterial.prototype.isMeshStandardMaterial = true;\n\n\tMeshStandardMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.defines = { 'STANDARD': '' };\n\n\t\tthis.color.copy( source.color );\n\t\tthis.roughness = source.roughness;\n\t\tthis.metalness = source.metalness;\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.emissive.copy( source.emissive );\n\t\tthis.emissiveMap = source.emissiveMap;\n\t\tthis.emissiveIntensity = source.emissiveIntensity;\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.roughnessMap = source.roughnessMap;\n\n\t\tthis.metalnessMap = source.metalnessMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.envMapIntensity = source.envMapIntensity;\n\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.skinning = source.skinning;\n\t\tthis.morphTargets = source.morphTargets;\n\t\tthis.morphNormals = source.morphNormals;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author WestLangley / http://github.com/WestLangley\n\t *\n\t * parameters = {\n\t * reflectivity: <float>\n\t * }\n\t */\n\n\tfunction MeshPhysicalMaterial( parameters ) {\n\n\t\tMeshStandardMaterial.call( this );\n\n\t\tthis.defines = { 'PHYSICAL': '' };\n\n\t\tthis.type = 'MeshPhysicalMaterial';\n\n\t\tthis.reflectivity = 0.5; // maps to F0 = 0.04\n\n\t\tthis.clearCoat = 0.0;\n\t\tthis.clearCoatRoughness = 0.0;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tMeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype );\n\tMeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial;\n\n\tMeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true;\n\n\tMeshPhysicalMaterial.prototype.copy = function ( source ) {\n\n\t\tMeshStandardMaterial.prototype.copy.call( this, source );\n\n\t\tthis.defines = { 'PHYSICAL': '' };\n\n\t\tthis.reflectivity = source.reflectivity;\n\n\t\tthis.clearCoat = source.clearCoat;\n\t\tthis.clearCoatRoughness = source.clearCoatRoughness;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * specular: <hex>,\n\t * shininess: <float>,\n\t * opacity: <float>,\n\t *\n\t * map: new THREE.Texture( <Image> ),\n\t *\n\t * lightMap: new THREE.Texture( <Image> ),\n\t * lightMapIntensity: <float>\n\t *\n\t * aoMap: new THREE.Texture( <Image> ),\n\t * aoMapIntensity: <float>\n\t *\n\t * emissive: <hex>,\n\t * emissiveIntensity: <float>\n\t * emissiveMap: new THREE.Texture( <Image> ),\n\t *\n\t * bumpMap: new THREE.Texture( <Image> ),\n\t * bumpScale: <float>,\n\t *\n\t * normalMap: new THREE.Texture( <Image> ),\n\t * normalScale: <Vector2>,\n\t *\n\t * displacementMap: new THREE.Texture( <Image> ),\n\t * displacementScale: <float>,\n\t * displacementBias: <float>,\n\t *\n\t * specularMap: new THREE.Texture( <Image> ),\n\t *\n\t * alphaMap: new THREE.Texture( <Image> ),\n\t *\n\t * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),\n\t * combine: THREE.Multiply,\n\t * reflectivity: <float>,\n\t * refractionRatio: <float>,\n\t *\n\t * wireframe: <boolean>,\n\t * wireframeLinewidth: <float>,\n\t *\n\t * skinning: <bool>,\n\t * morphTargets: <bool>,\n\t * morphNormals: <bool>\n\t * }\n\t */\n\n\tfunction MeshPhongMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'MeshPhongMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // diffuse\n\t\tthis.specular = new Color( 0x111111 );\n\t\tthis.shininess = 30;\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.emissive = new Color( 0x000000 );\n\t\tthis.emissiveIntensity = 1.0;\n\t\tthis.emissiveMap = null;\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.specularMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.combine = MultiplyOperation;\n\t\tthis.reflectivity = 1;\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.skinning = false;\n\t\tthis.morphTargets = false;\n\t\tthis.morphNormals = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tMeshPhongMaterial.prototype = Object.create( Material.prototype );\n\tMeshPhongMaterial.prototype.constructor = MeshPhongMaterial;\n\n\tMeshPhongMaterial.prototype.isMeshPhongMaterial = true;\n\n\tMeshPhongMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.color.copy( source.color );\n\t\tthis.specular.copy( source.specular );\n\t\tthis.shininess = source.shininess;\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.emissive.copy( source.emissive );\n\t\tthis.emissiveMap = source.emissiveMap;\n\t\tthis.emissiveIntensity = source.emissiveIntensity;\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.specularMap = source.specularMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.combine = source.combine;\n\t\tthis.reflectivity = source.reflectivity;\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.skinning = source.skinning;\n\t\tthis.morphTargets = source.morphTargets;\n\t\tthis.morphNormals = source.morphNormals;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t *\n\t * parameters = {\n\t * opacity: <float>,\n\t *\n\t * wireframe: <boolean>,\n\t * wireframeLinewidth: <float>\n\t * }\n\t */\n\n\tfunction MeshNormalMaterial( parameters ) {\n\n\t\tMaterial.call( this, parameters );\n\n\t\tthis.type = 'MeshNormalMaterial';\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.fog = false;\n\t\tthis.lights = false;\n\t\tthis.morphTargets = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tMeshNormalMaterial.prototype = Object.create( Material.prototype );\n\tMeshNormalMaterial.prototype.constructor = MeshNormalMaterial;\n\n\tMeshNormalMaterial.prototype.isMeshNormalMaterial = true;\n\n\tMeshNormalMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * opacity: <float>,\n\t *\n\t * map: new THREE.Texture( <Image> ),\n\t *\n\t * lightMap: new THREE.Texture( <Image> ),\n\t * lightMapIntensity: <float>\n\t *\n\t * aoMap: new THREE.Texture( <Image> ),\n\t * aoMapIntensity: <float>\n\t *\n\t * emissive: <hex>,\n\t * emissiveIntensity: <float>\n\t * emissiveMap: new THREE.Texture( <Image> ),\n\t *\n\t * specularMap: new THREE.Texture( <Image> ),\n\t *\n\t * alphaMap: new THREE.Texture( <Image> ),\n\t *\n\t * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),\n\t * combine: THREE.Multiply,\n\t * reflectivity: <float>,\n\t * refractionRatio: <float>,\n\t *\n\t * wireframe: <boolean>,\n\t * wireframeLinewidth: <float>,\n\t *\n\t * skinning: <bool>,\n\t * morphTargets: <bool>,\n\t * morphNormals: <bool>\n\t * }\n\t */\n\n\tfunction MeshLambertMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'MeshLambertMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // diffuse\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.emissive = new Color( 0x000000 );\n\t\tthis.emissiveIntensity = 1.0;\n\t\tthis.emissiveMap = null;\n\n\t\tthis.specularMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.combine = MultiplyOperation;\n\t\tthis.reflectivity = 1;\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.skinning = false;\n\t\tthis.morphTargets = false;\n\t\tthis.morphNormals = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tMeshLambertMaterial.prototype = Object.create( Material.prototype );\n\tMeshLambertMaterial.prototype.constructor = MeshLambertMaterial;\n\n\tMeshLambertMaterial.prototype.isMeshLambertMaterial = true;\n\n\tMeshLambertMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.emissive.copy( source.emissive );\n\t\tthis.emissiveMap = source.emissiveMap;\n\t\tthis.emissiveIntensity = source.emissiveIntensity;\n\n\t\tthis.specularMap = source.specularMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.combine = source.combine;\n\t\tthis.reflectivity = source.reflectivity;\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.skinning = source.skinning;\n\t\tthis.morphTargets = source.morphTargets;\n\t\tthis.morphNormals = source.morphNormals;\n\n\t\treturn this;\n\n\t};\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t *\n\t * parameters = {\n\t * color: <hex>,\n\t * opacity: <float>,\n\t *\n\t * linewidth: <float>,\n\t *\n\t * scale: <float>,\n\t * dashSize: <float>,\n\t * gapSize: <float>\n\t * }\n\t */\n\n\tfunction LineDashedMaterial( parameters ) {\n\n\t\tMaterial.call( this );\n\n\t\tthis.type = 'LineDashedMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.linewidth = 1;\n\n\t\tthis.scale = 1;\n\t\tthis.dashSize = 3;\n\t\tthis.gapSize = 1;\n\n\t\tthis.lights = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tLineDashedMaterial.prototype = Object.create( Material.prototype );\n\tLineDashedMaterial.prototype.constructor = LineDashedMaterial;\n\n\tLineDashedMaterial.prototype.isLineDashedMaterial = true;\n\n\tLineDashedMaterial.prototype.copy = function ( source ) {\n\n\t\tMaterial.prototype.copy.call( this, source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.linewidth = source.linewidth;\n\n\t\tthis.scale = source.scale;\n\t\tthis.dashSize = source.dashSize;\n\t\tthis.gapSize = source.gapSize;\n\n\t\treturn this;\n\n\t};\n\n\n\n\tvar Materials = Object.freeze({\n\t\tShadowMaterial: ShadowMaterial,\n\t\tSpriteMaterial: SpriteMaterial,\n\t\tRawShaderMaterial: RawShaderMaterial,\n\t\tShaderMaterial: ShaderMaterial,\n\t\tPointsMaterial: PointsMaterial,\n\t\tMultiMaterial: MultiMaterial,\n\t\tMeshPhysicalMaterial: MeshPhysicalMaterial,\n\t\tMeshStandardMaterial: MeshStandardMaterial,\n\t\tMeshPhongMaterial: MeshPhongMaterial,\n\t\tMeshNormalMaterial: MeshNormalMaterial,\n\t\tMeshLambertMaterial: MeshLambertMaterial,\n\t\tMeshDepthMaterial: MeshDepthMaterial,\n\t\tMeshBasicMaterial: MeshBasicMaterial,\n\t\tLineDashedMaterial: LineDashedMaterial,\n\t\tLineBasicMaterial: LineBasicMaterial,\n\t\tMaterial: Material\n\t});\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tvar Cache = {\n\n\t\tenabled: false,\n\n\t\tfiles: {},\n\n\t\tadd: function ( key, file ) {\n\n\t\t\tif ( this.enabled === false ) return;\n\n\t\t\t// console.log( 'THREE.Cache', 'Adding key:', key );\n\n\t\t\tthis.files[ key ] = file;\n\n\t\t},\n\n\t\tget: function ( key ) {\n\n\t\t\tif ( this.enabled === false ) return;\n\n\t\t\t// console.log( 'THREE.Cache', 'Checking key:', key );\n\n\t\t\treturn this.files[ key ];\n\n\t\t},\n\n\t\tremove: function ( key ) {\n\n\t\t\tdelete this.files[ key ];\n\n\t\t},\n\n\t\tclear: function () {\n\n\t\t\tthis.files = {};\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction LoadingManager( onLoad, onProgress, onError ) {\n\n\t\tvar scope = this;\n\n\t\tvar isLoading = false, itemsLoaded = 0, itemsTotal = 0;\n\n\t\tthis.onStart = undefined;\n\t\tthis.onLoad = onLoad;\n\t\tthis.onProgress = onProgress;\n\t\tthis.onError = onError;\n\n\t\tthis.itemStart = function ( url ) {\n\n\t\t\titemsTotal ++;\n\n\t\t\tif ( isLoading === false ) {\n\n\t\t\t\tif ( scope.onStart !== undefined ) {\n\n\t\t\t\t\tscope.onStart( url, itemsLoaded, itemsTotal );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tisLoading = true;\n\n\t\t};\n\n\t\tthis.itemEnd = function ( url ) {\n\n\t\t\titemsLoaded ++;\n\n\t\t\tif ( scope.onProgress !== undefined ) {\n\n\t\t\t\tscope.onProgress( url, itemsLoaded, itemsTotal );\n\n\t\t\t}\n\n\t\t\tif ( itemsLoaded === itemsTotal ) {\n\n\t\t\t\tisLoading = false;\n\n\t\t\t\tif ( scope.onLoad !== undefined ) {\n\n\t\t\t\t\tscope.onLoad();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.itemError = function ( url ) {\n\n\t\t\tif ( scope.onError !== undefined ) {\n\n\t\t\t\tscope.onError( url );\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\tvar DefaultLoadingManager = new LoadingManager();\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction XHRLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t}\n\n\tObject.assign( XHRLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tif ( url === undefined ) url = '';\n\n\t\t\tif ( this.path !== undefined ) url = this.path + url;\n\n\t\t\tvar scope = this;\n\n\t\t\tvar cached = Cache.get( url );\n\n\t\t\tif ( cached !== undefined ) {\n\n\t\t\t\tscope.manager.itemStart( url );\n\n\t\t\t\tsetTimeout( function () {\n\n\t\t\t\t\tif ( onLoad ) onLoad( cached );\n\n\t\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t\t}, 0 );\n\n\t\t\t\treturn cached;\n\n\t\t\t}\n\n\t\t\t// Check for data: URI\n\t\t\tvar dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;\n\t\t\tvar dataUriRegexResult = url.match( dataUriRegex );\n\n\t\t\t// Safari can not handle Data URIs through XMLHttpRequest so process manually\n\t\t\tif ( dataUriRegexResult ) {\n\n\t\t\t\tvar mimeType = dataUriRegexResult[1];\n\t\t\t\tvar isBase64 = !!dataUriRegexResult[2];\n\t\t\t\tvar data = dataUriRegexResult[3];\n\n\t\t\t\tdata = window.decodeURIComponent(data);\n\n\t\t\t\tif( isBase64 ) {\n\t\t\t\t\tdata = window.atob(data);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\n\t\t\t\t\tvar response;\n\t\t\t\t\tvar responseType = ( this.responseType || '' ).toLowerCase();\n\n\t\t\t\t\tswitch ( responseType ) {\n\n\t\t\t\t\t\tcase 'arraybuffer':\n\t\t\t\t\t\tcase 'blob':\n\n\t\t\t\t\t\t \tresponse = new ArrayBuffer( data.length );\n\t\t\t\t\t\t\tvar view = new Uint8Array( response );\n\t\t\t\t\t\t\tfor ( var i = 0; i < data.length; i ++ ) {\n\n\t\t\t\t\t\t\t\t\tview[ i ] = data.charCodeAt( i );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( responseType === 'blob' ) {\n\n\t\t\t\t\t\t\t\tresponse = new Blob( [ response ], { \"type\" : mimeType } );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'document':\n\n\t\t\t\t\t\t\tvar parser = new DOMParser();\n\t\t\t\t\t\t\tresponse = parser.parseFromString( data, mimeType );\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'json':\n\n\t\t\t\t\t\t\tresponse = JSON.parse( data );\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault: // 'text' or other\n\n\t\t\t\t\t\t\tresponse = data;\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wait for next browser tick\n\t\t\t\t\twindow.setTimeout( function() {\n\n\t\t\t\t\t\tif ( onLoad ) onLoad( response );\n\n\t\t\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t\t\t}, 0);\n\n\t\t\t\t} catch ( error ) {\n\n\t\t\t\t\t// Wait for next browser tick\n\t\t\t\t\twindow.setTimeout( function() {\n\n\t\t\t\t\t\tif ( onError ) onError( error );\n\n\t\t\t\t\t\tscope.manager.itemError( url );\n\n\t\t\t\t\t}, 0);\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tvar request = new XMLHttpRequest();\n\t\t\t\trequest.open( 'GET', url, true );\n\n\t\t\t\trequest.addEventListener( 'load', function ( event ) {\n\n\t\t\t\t\tvar response = event.target.response;\n\n\t\t\t\t\tCache.add( url, response );\n\n\t\t\t\t\tif ( this.status === 200 ) {\n\n\t\t\t\t\t\tif ( onLoad ) onLoad( response );\n\n\t\t\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t\t\t} else if ( this.status === 0 ) {\n\n\t\t\t\t\t\t// Some browsers return HTTP Status 0 when using non-http protocol\n\t\t\t\t\t\t// e.g. 'file://' or 'data://'. Handle as success.\n\n\t\t\t\t\t\tconsole.warn( 'THREE.XHRLoader: HTTP Status 0 received.' );\n\n\t\t\t\t\t\tif ( onLoad ) onLoad( response );\n\n\t\t\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( onError ) onError( event );\n\n\t\t\t\t\t\tscope.manager.itemError( url );\n\n\t\t\t\t\t}\n\n\t\t\t\t}, false );\n\n\t\t\t\tif ( onProgress !== undefined ) {\n\n\t\t\t\t\trequest.addEventListener( 'progress', function ( event ) {\n\n\t\t\t\t\t\tonProgress( event );\n\n\t\t\t\t\t}, false );\n\n\t\t\t\t}\n\n\t\t\t\trequest.addEventListener( 'error', function ( event ) {\n\n\t\t\t\t\tif ( onError ) onError( event );\n\n\t\t\t\t\tscope.manager.itemError( url );\n\n\t\t\t\t}, false );\n\n\t\t\t\tif ( this.responseType !== undefined ) request.responseType = this.responseType;\n\t\t\t\tif ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;\n\n\t\t\t\tif ( request.overrideMimeType ) request.overrideMimeType( 'text/plain' );\n\n\t\t\t\trequest.send( null );\n\n\t\t\t}\n\n\t\t\tscope.manager.itemStart( url );\n\n\t\t\treturn request;\n\n\t\t},\n\n\t\tsetPath: function ( value ) {\n\n\t\t\tthis.path = value;\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetResponseType: function ( value ) {\n\n\t\t\tthis.responseType = value;\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetWithCredentials: function ( value ) {\n\n\t\t\tthis.withCredentials = value;\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t *\n\t * Abstract Base class to block based textures loader (dds, pvr, ...)\n\t */\n\n\tfunction CompressedTextureLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t\t// override in sub classes\n\t\tthis._parser = null;\n\n\t}\n\n\tObject.assign( CompressedTextureLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tvar scope = this;\n\n\t\t\tvar images = [];\n\n\t\t\tvar texture = new CompressedTexture();\n\t\t\ttexture.image = images;\n\n\t\t\tvar loader = new XHRLoader( this.manager );\n\t\t\tloader.setPath( this.path );\n\t\t\tloader.setResponseType( 'arraybuffer' );\n\n\t\t\tfunction loadTexture( i ) {\n\n\t\t\t\tloader.load( url[ i ], function ( buffer ) {\n\n\t\t\t\t\tvar texDatas = scope._parser( buffer, true );\n\n\t\t\t\t\timages[ i ] = {\n\t\t\t\t\t\twidth: texDatas.width,\n\t\t\t\t\t\theight: texDatas.height,\n\t\t\t\t\t\tformat: texDatas.format,\n\t\t\t\t\t\tmipmaps: texDatas.mipmaps\n\t\t\t\t\t};\n\n\t\t\t\t\tloaded += 1;\n\n\t\t\t\t\tif ( loaded === 6 ) {\n\n\t\t\t\t\t\tif ( texDatas.mipmapCount === 1 )\n\t\t\t\t\t\t\ttexture.minFilter = LinearFilter;\n\n\t\t\t\t\t\ttexture.format = texDatas.format;\n\t\t\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t\t\tif ( onLoad ) onLoad( texture );\n\n\t\t\t\t\t}\n\n\t\t\t\t}, onProgress, onError );\n\n\t\t\t}\n\n\t\t\tif ( Array.isArray( url ) ) {\n\n\t\t\t\tvar loaded = 0;\n\n\t\t\t\tfor ( var i = 0, il = url.length; i < il; ++ i ) {\n\n\t\t\t\t\tloadTexture( i );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// compressed cubemap texture stored in a single DDS file\n\n\t\t\t\tloader.load( url, function ( buffer ) {\n\n\t\t\t\t\tvar texDatas = scope._parser( buffer, true );\n\n\t\t\t\t\tif ( texDatas.isCubemap ) {\n\n\t\t\t\t\t\tvar faces = texDatas.mipmaps.length / texDatas.mipmapCount;\n\n\t\t\t\t\t\tfor ( var f = 0; f < faces; f ++ ) {\n\n\t\t\t\t\t\t\timages[ f ] = { mipmaps : [] };\n\n\t\t\t\t\t\t\tfor ( var i = 0; i < texDatas.mipmapCount; i ++ ) {\n\n\t\t\t\t\t\t\t\timages[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );\n\t\t\t\t\t\t\t\timages[ f ].format = texDatas.format;\n\t\t\t\t\t\t\t\timages[ f ].width = texDatas.width;\n\t\t\t\t\t\t\t\timages[ f ].height = texDatas.height;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ttexture.image.width = texDatas.width;\n\t\t\t\t\t\ttexture.image.height = texDatas.height;\n\t\t\t\t\t\ttexture.mipmaps = texDatas.mipmaps;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( texDatas.mipmapCount === 1 ) {\n\n\t\t\t\t\t\ttexture.minFilter = LinearFilter;\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.format = texDatas.format;\n\t\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t\tif ( onLoad ) onLoad( texture );\n\n\t\t\t\t}, onProgress, onError );\n\n\t\t\t}\n\n\t\t\treturn texture;\n\n\t\t},\n\n\t\tsetPath: function ( value ) {\n\n\t\t\tthis.path = value;\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author Nikos M. / https://github.com/foo123/\n\t *\n\t * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)\n\t */\n\n\tvar DataTextureLoader = BinaryTextureLoader;\n\tfunction BinaryTextureLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t\t// override in sub classes\n\t\tthis._parser = null;\n\n\t}\n\n\tObject.assign( BinaryTextureLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tvar scope = this;\n\n\t\t\tvar texture = new DataTexture();\n\n\t\t\tvar loader = new XHRLoader( this.manager );\n\t\t\tloader.setResponseType( 'arraybuffer' );\n\n\t\t\tloader.load( url, function ( buffer ) {\n\n\t\t\t\tvar texData = scope._parser( buffer );\n\n\t\t\t\tif ( ! texData ) return;\n\n\t\t\t\tif ( undefined !== texData.image ) {\n\n\t\t\t\t\ttexture.image = texData.image;\n\n\t\t\t\t} else if ( undefined !== texData.data ) {\n\n\t\t\t\t\ttexture.image.width = texData.width;\n\t\t\t\t\ttexture.image.height = texData.height;\n\t\t\t\t\ttexture.image.data = texData.data;\n\n\t\t\t\t}\n\n\t\t\t\ttexture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping;\n\t\t\t\ttexture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping;\n\n\t\t\t\ttexture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter;\n\t\t\t\ttexture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter;\n\n\t\t\t\ttexture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;\n\n\t\t\t\tif ( undefined !== texData.format ) {\n\n\t\t\t\t\ttexture.format = texData.format;\n\n\t\t\t\t}\n\t\t\t\tif ( undefined !== texData.type ) {\n\n\t\t\t\t\ttexture.type = texData.type;\n\n\t\t\t\t}\n\n\t\t\t\tif ( undefined !== texData.mipmaps ) {\n\n\t\t\t\t\ttexture.mipmaps = texData.mipmaps;\n\n\t\t\t\t}\n\n\t\t\t\tif ( 1 === texData.mipmapCount ) {\n\n\t\t\t\t\ttexture.minFilter = LinearFilter;\n\n\t\t\t\t}\n\n\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\tif ( onLoad ) onLoad( texture, texData );\n\n\t\t\t}, onProgress, onError );\n\n\n\t\t\treturn texture;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction ImageLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t}\n\n\tObject.assign( ImageLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tvar scope = this;\n\n\t\t\tvar image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' );\n\t\t\timage.onload = function () {\n\n\t\t\t\timage.onload = null;\n\n\t\t\t\tURL.revokeObjectURL( image.src );\n\n\t\t\t\tif ( onLoad ) onLoad( image );\n\n\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t};\n\t\t\timage.onerror = onError;\n\n\t\t\tif ( url.indexOf( 'data:' ) === 0 ) {\n\n\t\t\t\timage.src = url;\n\n\t\t\t} else {\n\n\t\t\t\tvar loader = new XHRLoader();\n\t\t\t\tloader.setPath( this.path );\n\t\t\t\tloader.setResponseType( 'blob' );\n\t\t\t\tloader.setWithCredentials( this.withCredentials );\n\t\t\t\tloader.load( url, function ( blob ) {\n\n\t\t\t\t\timage.src = URL.createObjectURL( blob );\n\n\t\t\t\t}, onProgress, onError );\n\n\t\t\t}\n\n\t\t\tscope.manager.itemStart( url );\n\n\t\t\treturn image;\n\n\t\t},\n\n\t\tsetCrossOrigin: function ( value ) {\n\n\t\t\tthis.crossOrigin = value;\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetWithCredentials: function ( value ) {\n\n\t\t\tthis.withCredentials = value;\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetPath: function ( value ) {\n\n\t\t\tthis.path = value;\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction CubeTextureLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t}\n\n\tObject.assign( CubeTextureLoader.prototype, {\n\n\t\tload: function ( urls, onLoad, onProgress, onError ) {\n\n\t\t\tvar texture = new CubeTexture();\n\n\t\t\tvar loader = new ImageLoader( this.manager );\n\t\t\tloader.setCrossOrigin( this.crossOrigin );\n\t\t\tloader.setPath( this.path );\n\n\t\t\tvar loaded = 0;\n\n\t\t\tfunction loadTexture( i ) {\n\n\t\t\t\tloader.load( urls[ i ], function ( image ) {\n\n\t\t\t\t\ttexture.images[ i ] = image;\n\n\t\t\t\t\tloaded ++;\n\n\t\t\t\t\tif ( loaded === 6 ) {\n\n\t\t\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t\t\tif ( onLoad ) onLoad( texture );\n\n\t\t\t\t\t}\n\n\t\t\t\t}, undefined, onError );\n\n\t\t\t}\n\n\t\t\tfor ( var i = 0; i < urls.length; ++ i ) {\n\n\t\t\t\tloadTexture( i );\n\n\t\t\t}\n\n\t\t\treturn texture;\n\n\t\t},\n\n\t\tsetCrossOrigin: function ( value ) {\n\n\t\t\tthis.crossOrigin = value;\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetPath: function ( value ) {\n\n\t\t\tthis.path = value;\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction TextureLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t}\n\n\tObject.assign( TextureLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tvar texture = new Texture();\n\n\t\t\tvar loader = new ImageLoader( this.manager );\n\t\t\tloader.setCrossOrigin( this.crossOrigin );\n\t\t\tloader.setWithCredentials( this.withCredentials );\n\t\t\tloader.setPath( this.path );\n\t\t\tloader.load( url, function ( image ) {\n\n\t\t\t\t// JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB.\n\t\t\t\tvar isJPEG = url.search( /\\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\\:image\\/jpeg/ ) === 0;\n\n\t\t\t\ttexture.format = isJPEG ? RGBFormat : RGBAFormat;\n\t\t\t\ttexture.image = image;\n\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\tif ( onLoad !== undefined ) {\n\n\t\t\t\t\tonLoad( texture );\n\n\t\t\t\t}\n\n\t\t\t}, onProgress, onError );\n\n\t\t\treturn texture;\n\n\t\t},\n\n\t\tsetCrossOrigin: function ( value ) {\n\n\t\t\tthis.crossOrigin = value;\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetWithCredentials: function ( value ) {\n\n\t\t\tthis.withCredentials = value;\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetPath: function ( value ) {\n\n\t\t\tthis.path = value;\n\t\t\treturn this;\n\n\t\t}\n\n\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction Light( color, intensity ) {\n\n\t\tObject3D.call( this );\n\n\t\tthis.type = 'Light';\n\n\t\tthis.color = new Color( color );\n\t\tthis.intensity = intensity !== undefined ? intensity : 1;\n\n\t\tthis.receiveShadow = undefined;\n\n\t}\n\n\tLight.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n\t\tconstructor: Light,\n\n\t\tisLight: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tObject3D.prototype.copy.call( this, source );\n\n\t\t\tthis.color.copy( source.color );\n\t\t\tthis.intensity = source.intensity;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\ttoJSON: function ( meta ) {\n\n\t\t\tvar data = Object3D.prototype.toJSON.call( this, meta );\n\n\t\t\tdata.object.color = this.color.getHex();\n\t\t\tdata.object.intensity = this.intensity;\n\n\t\t\tif ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();\n\n\t\t\tif ( this.distance !== undefined ) data.object.distance = this.distance;\n\t\t\tif ( this.angle !== undefined ) data.object.angle = this.angle;\n\t\t\tif ( this.decay !== undefined ) data.object.decay = this.decay;\n\t\t\tif ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra;\n\n\t\t\tif ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON();\n\n\t\t\treturn data;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction HemisphereLight( skyColor, groundColor, intensity ) {\n\n\t\tLight.call( this, skyColor, intensity );\n\n\t\tthis.type = 'HemisphereLight';\n\n\t\tthis.castShadow = undefined;\n\n\t\tthis.position.copy( Object3D.DefaultUp );\n\t\tthis.updateMatrix();\n\n\t\tthis.groundColor = new Color( groundColor );\n\n\t}\n\n\tHemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n\t\tconstructor: HemisphereLight,\n\n\t\tisHemisphereLight: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tLight.prototype.copy.call( this, source );\n\n\t\t\tthis.groundColor.copy( source.groundColor );\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction LightShadow( camera ) {\n\n\t\tthis.camera = camera;\n\n\t\tthis.bias = 0;\n\t\tthis.radius = 1;\n\n\t\tthis.mapSize = new Vector2( 512, 512 );\n\n\t\tthis.map = null;\n\t\tthis.matrix = new Matrix4();\n\n\t}\n\n\tObject.assign( LightShadow.prototype, {\n\n\t\tcopy: function ( source ) {\n\n\t\t\tthis.camera = source.camera.clone();\n\n\t\t\tthis.bias = source.bias;\n\t\t\tthis.radius = source.radius;\n\n\t\t\tthis.mapSize.copy( source.mapSize );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tclone: function () {\n\n\t\t\treturn new this.constructor().copy( this );\n\n\t\t},\n\n\t\ttoJSON: function () {\n\n\t\t\tvar object = {};\n\n\t\t\tif ( this.bias !== 0 ) object.bias = this.bias;\n\t\t\tif ( this.radius !== 1 ) object.radius = this.radius;\n\t\t\tif ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray();\n\n\t\t\tobject.camera = this.camera.toJSON( false ).object;\n\t\t\tdelete object.camera.matrix;\n\n\t\t\treturn object;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction SpotLightShadow() {\n\n\t\tLightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) );\n\n\t}\n\n\tSpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {\n\n\t\tconstructor: SpotLightShadow,\n\n\t\tisSpotLightShadow: true,\n\n\t\tupdate: function ( light ) {\n\n\t\t\tvar fov = _Math.RAD2DEG * 2 * light.angle;\n\t\t\tvar aspect = this.mapSize.width / this.mapSize.height;\n\t\t\tvar far = light.distance || 500;\n\n\t\t\tvar camera = this.camera;\n\n\t\t\tif ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) {\n\n\t\t\t\tcamera.fov = fov;\n\t\t\t\tcamera.aspect = aspect;\n\t\t\t\tcamera.far = far;\n\t\t\t\tcamera.updateProjectionMatrix();\n\n\t\t\t}\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction SpotLight( color, intensity, distance, angle, penumbra, decay ) {\n\n\t\tLight.call( this, color, intensity );\n\n\t\tthis.type = 'SpotLight';\n\n\t\tthis.position.copy( Object3D.DefaultUp );\n\t\tthis.updateMatrix();\n\n\t\tthis.target = new Object3D();\n\n\t\tObject.defineProperty( this, 'power', {\n\t\t\tget: function () {\n\t\t\t\t// intensity = power per solid angle.\n\t\t\t\t// ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf\n\t\t\t\treturn this.intensity * Math.PI;\n\t\t\t},\n\t\t\tset: function ( power ) {\n\t\t\t\t// intensity = power per solid angle.\n\t\t\t\t// ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf\n\t\t\t\tthis.intensity = power / Math.PI;\n\t\t\t}\n\t\t} );\n\n\t\tthis.distance = ( distance !== undefined ) ? distance : 0;\n\t\tthis.angle = ( angle !== undefined ) ? angle : Math.PI / 3;\n\t\tthis.penumbra = ( penumbra !== undefined ) ? penumbra : 0;\n\t\tthis.decay = ( decay !== undefined ) ? decay : 1;\t// for physically correct lights, should be 2.\n\n\t\tthis.shadow = new SpotLightShadow();\n\n\t}\n\n\tSpotLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n\t\tconstructor: SpotLight,\n\n\t\tisSpotLight: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tLight.prototype.copy.call( this, source );\n\n\t\t\tthis.distance = source.distance;\n\t\t\tthis.angle = source.angle;\n\t\t\tthis.penumbra = source.penumbra;\n\t\t\tthis.decay = source.decay;\n\n\t\t\tthis.target = source.target.clone();\n\n\t\t\tthis.shadow = source.shadow.clone();\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\n\tfunction PointLight( color, intensity, distance, decay ) {\n\n\t\tLight.call( this, color, intensity );\n\n\t\tthis.type = 'PointLight';\n\n\t\tObject.defineProperty( this, 'power', {\n\t\t\tget: function () {\n\t\t\t\t// intensity = power per solid angle.\n\t\t\t\t// ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf\n\t\t\t\treturn this.intensity * 4 * Math.PI;\n\n\t\t\t},\n\t\t\tset: function ( power ) {\n\t\t\t\t// intensity = power per solid angle.\n\t\t\t\t// ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf\n\t\t\t\tthis.intensity = power / ( 4 * Math.PI );\n\t\t\t}\n\t\t} );\n\n\t\tthis.distance = ( distance !== undefined ) ? distance : 0;\n\t\tthis.decay = ( decay !== undefined ) ? decay : 1;\t// for physically correct lights, should be 2.\n\n\t\tthis.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) );\n\n\t}\n\n\tPointLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n\t\tconstructor: PointLight,\n\n\t\tisPointLight: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tLight.prototype.copy.call( this, source );\n\n\t\t\tthis.distance = source.distance;\n\t\t\tthis.decay = source.decay;\n\n\t\t\tthis.shadow = source.shadow.clone();\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction DirectionalLightShadow( light ) {\n\n\t\tLightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );\n\n\t}\n\n\tDirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {\n\n\t\tconstructor: DirectionalLightShadow\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction DirectionalLight( color, intensity ) {\n\n\t\tLight.call( this, color, intensity );\n\n\t\tthis.type = 'DirectionalLight';\n\n\t\tthis.position.copy( Object3D.DefaultUp );\n\t\tthis.updateMatrix();\n\n\t\tthis.target = new Object3D();\n\n\t\tthis.shadow = new DirectionalLightShadow();\n\n\t}\n\n\tDirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n\t\tconstructor: DirectionalLight,\n\n\t\tisDirectionalLight: true,\n\n\t\tcopy: function ( source ) {\n\n\t\t\tLight.prototype.copy.call( this, source );\n\n\t\t\tthis.target = source.target.clone();\n\n\t\t\tthis.shadow = source.shadow.clone();\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction AmbientLight( color, intensity ) {\n\n\t\tLight.call( this, color, intensity );\n\n\t\tthis.type = 'AmbientLight';\n\n\t\tthis.castShadow = undefined;\n\n\t}\n\n\tAmbientLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n\t\tconstructor: AmbientLight,\n\n\t\tisAmbientLight: true,\n\n\t} );\n\n\t/**\n\t * @author tschw\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t */\n\n\tvar AnimationUtils = {\n\n\t\t// same as Array.prototype.slice, but also works on typed arrays\n\t\tarraySlice: function( array, from, to ) {\n\n\t\t\tif ( AnimationUtils.isTypedArray( array ) ) {\n\n\t\t\t\treturn new array.constructor( array.subarray( from, to ) );\n\n\t\t\t}\n\n\t\t\treturn array.slice( from, to );\n\n\t\t},\n\n\t\t// converts an array to a specific type\n\t\tconvertArray: function( array, type, forceClone ) {\n\n\t\t\tif ( ! array || // let 'undefined' and 'null' pass\n\t\t\t\t\t! forceClone && array.constructor === type ) return array;\n\n\t\t\tif ( typeof type.BYTES_PER_ELEMENT === 'number' ) {\n\n\t\t\t\treturn new type( array ); // create typed array\n\n\t\t\t}\n\n\t\t\treturn Array.prototype.slice.call( array ); // create Array\n\n\t\t},\n\n\t\tisTypedArray: function( object ) {\n\n\t\t\treturn ArrayBuffer.isView( object ) &&\n\t\t\t\t\t! ( object instanceof DataView );\n\n\t\t},\n\n\t\t// returns an array by which times and values can be sorted\n\t\tgetKeyframeOrder: function( times ) {\n\n\t\t\tfunction compareTime( i, j ) {\n\n\t\t\t\treturn times[ i ] - times[ j ];\n\n\t\t\t}\n\n\t\t\tvar n = times.length;\n\t\t\tvar result = new Array( n );\n\t\t\tfor ( var i = 0; i !== n; ++ i ) result[ i ] = i;\n\n\t\t\tresult.sort( compareTime );\n\n\t\t\treturn result;\n\n\t\t},\n\n\t\t// uses the array previously returned by 'getKeyframeOrder' to sort data\n\t\tsortedArray: function( values, stride, order ) {\n\n\t\t\tvar nValues = values.length;\n\t\t\tvar result = new values.constructor( nValues );\n\n\t\t\tfor ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {\n\n\t\t\t\tvar srcOffset = order[ i ] * stride;\n\n\t\t\t\tfor ( var j = 0; j !== stride; ++ j ) {\n\n\t\t\t\t\tresult[ dstOffset ++ ] = values[ srcOffset + j ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t},\n\n\t\t// function for parsing AOS keyframe formats\n\t\tflattenJSON: function( jsonKeys, times, values, valuePropertyName ) {\n\n\t\t\tvar i = 1, key = jsonKeys[ 0 ];\n\n\t\t\twhile ( key !== undefined && key[ valuePropertyName ] === undefined ) {\n\n\t\t\t\tkey = jsonKeys[ i ++ ];\n\n\t\t\t}\n\n\t\t\tif ( key === undefined ) return; // no data\n\n\t\t\tvar value = key[ valuePropertyName ];\n\t\t\tif ( value === undefined ) return; // no data\n\n\t\t\tif ( Array.isArray( value ) ) {\n\n\t\t\t\tdo {\n\n\t\t\t\t\tvalue = key[ valuePropertyName ];\n\n\t\t\t\t\tif ( value !== undefined ) {\n\n\t\t\t\t\t\ttimes.push( key.time );\n\t\t\t\t\t\tvalues.push.apply( values, value ); // push all elements\n\n\t\t\t\t\t}\n\n\t\t\t\t\tkey = jsonKeys[ i ++ ];\n\n\t\t\t\t} while ( key !== undefined );\n\n\t\t\t} else if ( value.toArray !== undefined ) {\n\t\t\t\t// ...assume THREE.Math-ish\n\n\t\t\t\tdo {\n\n\t\t\t\t\tvalue = key[ valuePropertyName ];\n\n\t\t\t\t\tif ( value !== undefined ) {\n\n\t\t\t\t\t\ttimes.push( key.time );\n\t\t\t\t\t\tvalue.toArray( values, values.length );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tkey = jsonKeys[ i ++ ];\n\n\t\t\t\t} while ( key !== undefined );\n\n\t\t\t} else {\n\t\t\t\t// otherwise push as-is\n\n\t\t\t\tdo {\n\n\t\t\t\t\tvalue = key[ valuePropertyName ];\n\n\t\t\t\t\tif ( value !== undefined ) {\n\n\t\t\t\t\t\ttimes.push( key.time );\n\t\t\t\t\t\tvalues.push( value );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tkey = jsonKeys[ i ++ ];\n\n\t\t\t\t} while ( key !== undefined );\n\n\t\t\t}\n\n\t\t}\n\n\t};\n\n\t/**\n\t * Abstract base class of interpolants over parametric samples.\n\t *\n\t * The parameter domain is one dimensional, typically the time or a path\n\t * along a curve defined by the data.\n\t *\n\t * The sample values can have any dimensionality and derived classes may\n\t * apply special interpretations to the data.\n\t *\n\t * This class provides the interval seek in a Template Method, deferring\n\t * the actual interpolation to derived classes.\n\t *\n\t * Time complexity is O(1) for linear access crossing at most two points\n\t * and O(log N) for random access, where N is the number of positions.\n\t *\n\t * References:\n\t *\n\t * \t\thttp://www.oodesign.com/template-method-pattern.html\n\t *\n\t * @author tschw\n\t */\n\n\tfunction Interpolant(\n\t\t\tparameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tthis.parameterPositions = parameterPositions;\n\t\tthis._cachedIndex = 0;\n\n\t\tthis.resultBuffer = resultBuffer !== undefined ?\n\t\t\t\tresultBuffer : new sampleValues.constructor( sampleSize );\n\t\tthis.sampleValues = sampleValues;\n\t\tthis.valueSize = sampleSize;\n\n\t}\n\n\tInterpolant.prototype = {\n\n\t\tconstructor: Interpolant,\n\n\t\tevaluate: function( t ) {\n\n\t\t\tvar pp = this.parameterPositions,\n\t\t\t\ti1 = this._cachedIndex,\n\n\t\t\t\tt1 = pp[ i1 ],\n\t\t\t\tt0 = pp[ i1 - 1 ];\n\n\t\t\tvalidate_interval: {\n\n\t\t\t\tseek: {\n\n\t\t\t\t\tvar right;\n\n\t\t\t\t\tlinear_scan: {\n\t//- See http://jsperf.com/comparison-to-undefined/3\n\t//- slower code:\n\t//-\n\t//- \t\t\t\tif ( t >= t1 || t1 === undefined ) {\n\t\t\t\t\t\tforward_scan: if ( ! ( t < t1 ) ) {\n\n\t\t\t\t\t\t\tfor ( var giveUpAt = i1 + 2; ;) {\n\n\t\t\t\t\t\t\t\tif ( t1 === undefined ) {\n\n\t\t\t\t\t\t\t\t\tif ( t < t0 ) break forward_scan;\n\n\t\t\t\t\t\t\t\t\t// after end\n\n\t\t\t\t\t\t\t\t\ti1 = pp.length;\n\t\t\t\t\t\t\t\t\tthis._cachedIndex = i1;\n\t\t\t\t\t\t\t\t\treturn this.afterEnd_( i1 - 1, t, t0 );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif ( i1 === giveUpAt ) break; // this loop\n\n\t\t\t\t\t\t\t\tt0 = t1;\n\t\t\t\t\t\t\t\tt1 = pp[ ++ i1 ];\n\n\t\t\t\t\t\t\t\tif ( t < t1 ) {\n\n\t\t\t\t\t\t\t\t\t// we have arrived at the sought interval\n\t\t\t\t\t\t\t\t\tbreak seek;\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// prepare binary search on the right side of the index\n\t\t\t\t\t\t\tright = pp.length;\n\t\t\t\t\t\t\tbreak linear_scan;\n\n\t\t\t\t\t\t}\n\n\t//- slower code:\n\t//-\t\t\t\t\tif ( t < t0 || t0 === undefined ) {\n\t\t\t\t\t\tif ( ! ( t >= t0 ) ) {\n\n\t\t\t\t\t\t\t// looping?\n\n\t\t\t\t\t\t\tvar t1global = pp[ 1 ];\n\n\t\t\t\t\t\t\tif ( t < t1global ) {\n\n\t\t\t\t\t\t\t\ti1 = 2; // + 1, using the scan for the details\n\t\t\t\t\t\t\t\tt0 = t1global;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// linear reverse scan\n\n\t\t\t\t\t\t\tfor ( var giveUpAt = i1 - 2; ;) {\n\n\t\t\t\t\t\t\t\tif ( t0 === undefined ) {\n\n\t\t\t\t\t\t\t\t\t// before start\n\n\t\t\t\t\t\t\t\t\tthis._cachedIndex = 0;\n\t\t\t\t\t\t\t\t\treturn this.beforeStart_( 0, t, t1 );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif ( i1 === giveUpAt ) break; // this loop\n\n\t\t\t\t\t\t\t\tt1 = t0;\n\t\t\t\t\t\t\t\tt0 = pp[ -- i1 - 1 ];\n\n\t\t\t\t\t\t\t\tif ( t >= t0 ) {\n\n\t\t\t\t\t\t\t\t\t// we have arrived at the sought interval\n\t\t\t\t\t\t\t\t\tbreak seek;\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// prepare binary search on the left side of the index\n\t\t\t\t\t\t\tright = i1;\n\t\t\t\t\t\t\ti1 = 0;\n\t\t\t\t\t\t\tbreak linear_scan;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// the interval is valid\n\n\t\t\t\t\t\tbreak validate_interval;\n\n\t\t\t\t\t} // linear scan\n\n\t\t\t\t\t// binary search\n\n\t\t\t\t\twhile ( i1 < right ) {\n\n\t\t\t\t\t\tvar mid = ( i1 + right ) >>> 1;\n\n\t\t\t\t\t\tif ( t < pp[ mid ] ) {\n\n\t\t\t\t\t\t\tright = mid;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\ti1 = mid + 1;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tt1 = pp[ i1 ];\n\t\t\t\t\tt0 = pp[ i1 - 1 ];\n\n\t\t\t\t\t// check boundary cases, again\n\n\t\t\t\t\tif ( t0 === undefined ) {\n\n\t\t\t\t\t\tthis._cachedIndex = 0;\n\t\t\t\t\t\treturn this.beforeStart_( 0, t, t1 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( t1 === undefined ) {\n\n\t\t\t\t\t\ti1 = pp.length;\n\t\t\t\t\t\tthis._cachedIndex = i1;\n\t\t\t\t\t\treturn this.afterEnd_( i1 - 1, t0, t );\n\n\t\t\t\t\t}\n\n\t\t\t\t} // seek\n\n\t\t\t\tthis._cachedIndex = i1;\n\n\t\t\t\tthis.intervalChanged_( i1, t0, t1 );\n\n\t\t\t} // validate_interval\n\n\t\t\treturn this.interpolate_( i1, t0, t, t1 );\n\n\t\t},\n\n\t\tsettings: null, // optional, subclass-specific settings structure\n\t\t// Note: The indirection allows central control of many interpolants.\n\n\t\t// --- Protected interface\n\n\t\tDefaultSettings_: {},\n\n\t\tgetSettings_: function() {\n\n\t\t\treturn this.settings || this.DefaultSettings_;\n\n\t\t},\n\n\t\tcopySampleValue_: function( index ) {\n\n\t\t\t// copies a sample value to the result buffer\n\n\t\t\tvar result = this.resultBuffer,\n\t\t\t\tvalues = this.sampleValues,\n\t\t\t\tstride = this.valueSize,\n\t\t\t\toffset = index * stride;\n\n\t\t\tfor ( var i = 0; i !== stride; ++ i ) {\n\n\t\t\t\tresult[ i ] = values[ offset + i ];\n\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t},\n\n\t\t// Template methods for derived classes:\n\n\t\tinterpolate_: function( i1, t0, t, t1 ) {\n\n\t\t\tthrow new Error( \"call to abstract method\" );\n\t\t\t// implementations shall return this.resultBuffer\n\n\t\t},\n\n\t\tintervalChanged_: function( i1, t0, t1 ) {\n\n\t\t\t// empty\n\n\t\t}\n\n\t};\n\n\tObject.assign( Interpolant.prototype, {\n\n\t\tbeforeStart_: //( 0, t, t0 ), returns this.resultBuffer\n\t\t\tInterpolant.prototype.copySampleValue_,\n\n\t\tafterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer\n\t\t\tInterpolant.prototype.copySampleValue_\n\n\t} );\n\n\t/**\n\t * Fast and simple cubic spline interpolant.\n\t *\n\t * It was derived from a Hermitian construction setting the first derivative\n\t * at each sample position to the linear slope between neighboring positions\n\t * over their parameter interval.\n\t *\n\t * @author tschw\n\t */\n\n\tfunction CubicInterpolant(\n\t\t\tparameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tInterpolant.call(\n\t\t\t\tthis, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t\tthis._weightPrev = -0;\n\t\tthis._offsetPrev = -0;\n\t\tthis._weightNext = -0;\n\t\tthis._offsetNext = -0;\n\n\t}\n\n\tCubicInterpolant.prototype =\n\t\t\tObject.assign( Object.create( Interpolant.prototype ), {\n\n\t\tconstructor: CubicInterpolant,\n\n\t\tDefaultSettings_: {\n\n\t\t\tendingStart: \tZeroCurvatureEnding,\n\t\t\tendingEnd:\t\tZeroCurvatureEnding\n\n\t\t},\n\n\t\tintervalChanged_: function( i1, t0, t1 ) {\n\n\t\t\tvar pp = this.parameterPositions,\n\t\t\t\tiPrev = i1 - 2,\n\t\t\t\tiNext = i1 + 1,\n\n\t\t\t\ttPrev = pp[ iPrev ],\n\t\t\t\ttNext = pp[ iNext ];\n\n\t\t\tif ( tPrev === undefined ) {\n\n\t\t\t\tswitch ( this.getSettings_().endingStart ) {\n\n\t\t\t\t\tcase ZeroSlopeEnding:\n\n\t\t\t\t\t\t// f'(t0) = 0\n\t\t\t\t\t\tiPrev = i1;\n\t\t\t\t\t\ttPrev = 2 * t0 - t1;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase WrapAroundEnding:\n\n\t\t\t\t\t\t// use the other end of the curve\n\t\t\t\t\t\tiPrev = pp.length - 2;\n\t\t\t\t\t\ttPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault: // ZeroCurvatureEnding\n\n\t\t\t\t\t\t// f''(t0) = 0 a.k.a. Natural Spline\n\t\t\t\t\t\tiPrev = i1;\n\t\t\t\t\t\ttPrev = t1;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( tNext === undefined ) {\n\n\t\t\t\tswitch ( this.getSettings_().endingEnd ) {\n\n\t\t\t\t\tcase ZeroSlopeEnding:\n\n\t\t\t\t\t\t// f'(tN) = 0\n\t\t\t\t\t\tiNext = i1;\n\t\t\t\t\t\ttNext = 2 * t1 - t0;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase WrapAroundEnding:\n\n\t\t\t\t\t\t// use the other end of the curve\n\t\t\t\t\t\tiNext = 1;\n\t\t\t\t\t\ttNext = t1 + pp[ 1 ] - pp[ 0 ];\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault: // ZeroCurvatureEnding\n\n\t\t\t\t\t\t// f''(tN) = 0, a.k.a. Natural Spline\n\t\t\t\t\t\tiNext = i1 - 1;\n\t\t\t\t\t\ttNext = t0;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar halfDt = ( t1 - t0 ) * 0.5,\n\t\t\t\tstride = this.valueSize;\n\n\t\t\tthis._weightPrev = halfDt / ( t0 - tPrev );\n\t\t\tthis._weightNext = halfDt / ( tNext - t1 );\n\t\t\tthis._offsetPrev = iPrev * stride;\n\t\t\tthis._offsetNext = iNext * stride;\n\n\t\t},\n\n\t\tinterpolate_: function( i1, t0, t, t1 ) {\n\n\t\t\tvar result = this.resultBuffer,\n\t\t\t\tvalues = this.sampleValues,\n\t\t\t\tstride = this.valueSize,\n\n\t\t\t\to1 = i1 * stride,\t\to0 = o1 - stride,\n\t\t\t\toP = this._offsetPrev, \toN = this._offsetNext,\n\t\t\t\twP = this._weightPrev,\twN = this._weightNext,\n\n\t\t\t\tp = ( t - t0 ) / ( t1 - t0 ),\n\t\t\t\tpp = p * p,\n\t\t\t\tppp = pp * p;\n\n\t\t\t// evaluate polynomials\n\n\t\t\tvar sP = - wP * ppp + 2 * wP * pp - wP * p;\n\t\t\tvar s0 = ( 1 + wP ) * ppp + (-1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1;\n\t\t\tvar s1 = (-1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p;\n\t\t\tvar sN = wN * ppp - wN * pp;\n\n\t\t\t// combine data linearly\n\n\t\t\tfor ( var i = 0; i !== stride; ++ i ) {\n\n\t\t\t\tresult[ i ] =\n\t\t\t\t\t\tsP * values[ oP + i ] +\n\t\t\t\t\t\ts0 * values[ o0 + i ] +\n\t\t\t\t\t\ts1 * values[ o1 + i ] +\n\t\t\t\t\t\tsN * values[ oN + i ];\n\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author tschw\n\t */\n\n\tfunction LinearInterpolant(\n\t\t\tparameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tInterpolant.call(\n\t\t\t\tthis, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t}\n\n\tLinearInterpolant.prototype =\n\t\t\tObject.assign( Object.create( Interpolant.prototype ), {\n\n\t\tconstructor: LinearInterpolant,\n\n\t\tinterpolate_: function( i1, t0, t, t1 ) {\n\n\t\t\tvar result = this.resultBuffer,\n\t\t\t\tvalues = this.sampleValues,\n\t\t\t\tstride = this.valueSize,\n\n\t\t\t\toffset1 = i1 * stride,\n\t\t\t\toffset0 = offset1 - stride,\n\n\t\t\t\tweight1 = ( t - t0 ) / ( t1 - t0 ),\n\t\t\t\tweight0 = 1 - weight1;\n\n\t\t\tfor ( var i = 0; i !== stride; ++ i ) {\n\n\t\t\t\tresult[ i ] =\n\t\t\t\t\t\tvalues[ offset0 + i ] * weight0 +\n\t\t\t\t\t\tvalues[ offset1 + i ] * weight1;\n\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t *\n\t * Interpolant that evaluates to the sample value at the position preceeding\n\t * the parameter.\n\t *\n\t * @author tschw\n\t */\n\n\tfunction DiscreteInterpolant(\n\t\t\tparameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tInterpolant.call(\n\t\t\t\tthis, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t}\n\n\tDiscreteInterpolant.prototype =\n\t\t\tObject.assign( Object.create( Interpolant.prototype ), {\n\n\t\tconstructor: DiscreteInterpolant,\n\n\t\tinterpolate_: function( i1, t0, t, t1 ) {\n\n\t\t\treturn this.copySampleValue_( i1 - 1 );\n\n\t\t}\n\n\t} );\n\n\tvar KeyframeTrackPrototype;\n\n\tKeyframeTrackPrototype = {\n\n\t\tTimeBufferType: Float32Array,\n\t\tValueBufferType: Float32Array,\n\n\t\tDefaultInterpolation: InterpolateLinear,\n\n\t\tInterpolantFactoryMethodDiscrete: function( result ) {\n\n\t\t\treturn new DiscreteInterpolant(\n\t\t\t\t\tthis.times, this.values, this.getValueSize(), result );\n\n\t\t},\n\n\t\tInterpolantFactoryMethodLinear: function( result ) {\n\n\t\t\treturn new LinearInterpolant(\n\t\t\t\t\tthis.times, this.values, this.getValueSize(), result );\n\n\t\t},\n\n\t\tInterpolantFactoryMethodSmooth: function( result ) {\n\n\t\t\treturn new CubicInterpolant(\n\t\t\t\t\tthis.times, this.values, this.getValueSize(), result );\n\n\t\t},\n\n\t\tsetInterpolation: function( interpolation ) {\n\n\t\t\tvar factoryMethod;\n\n\t\t\tswitch ( interpolation ) {\n\n\t\t\t\tcase InterpolateDiscrete:\n\n\t\t\t\t\tfactoryMethod = this.InterpolantFactoryMethodDiscrete;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase InterpolateLinear:\n\n\t\t\t\t\tfactoryMethod = this.InterpolantFactoryMethodLinear;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase InterpolateSmooth:\n\n\t\t\t\t\tfactoryMethod = this.InterpolantFactoryMethodSmooth;\n\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tif ( factoryMethod === undefined ) {\n\n\t\t\t\tvar message = \"unsupported interpolation for \" +\n\t\t\t\t\t\tthis.ValueTypeName + \" keyframe track named \" + this.name;\n\n\t\t\t\tif ( this.createInterpolant === undefined ) {\n\n\t\t\t\t\t// fall back to default, unless the default itself is messed up\n\t\t\t\t\tif ( interpolation !== this.DefaultInterpolation ) {\n\n\t\t\t\t\t\tthis.setInterpolation( this.DefaultInterpolation );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tthrow new Error( message ); // fatal, in this case\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tconsole.warn( message );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tthis.createInterpolant = factoryMethod;\n\n\t\t},\n\n\t\tgetInterpolation: function() {\n\n\t\t\tswitch ( this.createInterpolant ) {\n\n\t\t\t\tcase this.InterpolantFactoryMethodDiscrete:\n\n\t\t\t\t\treturn InterpolateDiscrete;\n\n\t\t\t\tcase this.InterpolantFactoryMethodLinear:\n\n\t\t\t\t\treturn InterpolateLinear;\n\n\t\t\t\tcase this.InterpolantFactoryMethodSmooth:\n\n\t\t\t\t\treturn InterpolateSmooth;\n\n\t\t\t}\n\n\t\t},\n\n\t\tgetValueSize: function() {\n\n\t\t\treturn this.values.length / this.times.length;\n\n\t\t},\n\n\t\t// move all keyframes either forwards or backwards in time\n\t\tshift: function( timeOffset ) {\n\n\t\t\tif( timeOffset !== 0.0 ) {\n\n\t\t\t\tvar times = this.times;\n\n\t\t\t\tfor( var i = 0, n = times.length; i !== n; ++ i ) {\n\n\t\t\t\t\ttimes[ i ] += timeOffset;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// scale all keyframe times by a factor (useful for frame <-> seconds conversions)\n\t\tscale: function( timeScale ) {\n\n\t\t\tif( timeScale !== 1.0 ) {\n\n\t\t\t\tvar times = this.times;\n\n\t\t\t\tfor( var i = 0, n = times.length; i !== n; ++ i ) {\n\n\t\t\t\t\ttimes[ i ] *= timeScale;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// removes keyframes before and after animation without changing any values within the range [startTime, endTime].\n\t\t// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values\n\t\ttrim: function( startTime, endTime ) {\n\n\t\t\tvar times = this.times,\n\t\t\t\tnKeys = times.length,\n\t\t\t\tfrom = 0,\n\t\t\t\tto = nKeys - 1;\n\n\t\t\twhile ( from !== nKeys && times[ from ] < startTime ) ++ from;\n\t\t\twhile ( to !== -1 && times[ to ] > endTime ) -- to;\n\n\t\t\t++ to; // inclusive -> exclusive bound\n\n\t\t\tif( from !== 0 || to !== nKeys ) {\n\n\t\t\t\t// empty tracks are forbidden, so keep at least one keyframe\n\t\t\t\tif ( from >= to ) to = Math.max( to , 1 ), from = to - 1;\n\n\t\t\t\tvar stride = this.getValueSize();\n\t\t\t\tthis.times = AnimationUtils.arraySlice( times, from, to );\n\t\t\t\tthis.values = AnimationUtils.\n\t\t\t\t\t\tarraySlice( this.values, from * stride, to * stride );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable\n\t\tvalidate: function() {\n\n\t\t\tvar valid = true;\n\n\t\t\tvar valueSize = this.getValueSize();\n\t\t\tif ( valueSize - Math.floor( valueSize ) !== 0 ) {\n\n\t\t\t\tconsole.error( \"invalid value size in track\", this );\n\t\t\t\tvalid = false;\n\n\t\t\t}\n\n\t\t\tvar times = this.times,\n\t\t\t\tvalues = this.values,\n\n\t\t\t\tnKeys = times.length;\n\n\t\t\tif( nKeys === 0 ) {\n\n\t\t\t\tconsole.error( \"track is empty\", this );\n\t\t\t\tvalid = false;\n\n\t\t\t}\n\n\t\t\tvar prevTime = null;\n\n\t\t\tfor( var i = 0; i !== nKeys; i ++ ) {\n\n\t\t\t\tvar currTime = times[ i ];\n\n\t\t\t\tif ( typeof currTime === 'number' && isNaN( currTime ) ) {\n\n\t\t\t\t\tconsole.error( \"time is not a valid number\", this, i, currTime );\n\t\t\t\t\tvalid = false;\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t\tif( prevTime !== null && prevTime > currTime ) {\n\n\t\t\t\t\tconsole.error( \"out of order keys\", this, i, currTime, prevTime );\n\t\t\t\t\tvalid = false;\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t\tprevTime = currTime;\n\n\t\t\t}\n\n\t\t\tif ( values !== undefined ) {\n\n\t\t\t\tif ( AnimationUtils.isTypedArray( values ) ) {\n\n\t\t\t\t\tfor ( var i = 0, n = values.length; i !== n; ++ i ) {\n\n\t\t\t\t\t\tvar value = values[ i ];\n\n\t\t\t\t\t\tif ( isNaN( value ) ) {\n\n\t\t\t\t\t\t\tconsole.error( \"value is not a valid number\", this, i, value );\n\t\t\t\t\t\t\tvalid = false;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn valid;\n\n\t\t},\n\n\t\t// removes equivalent sequential keys as common in morph target sequences\n\t\t// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)\n\t\toptimize: function() {\n\n\t\t\tvar times = this.times,\n\t\t\t\tvalues = this.values,\n\t\t\t\tstride = this.getValueSize(),\n\n\t\t\t\tsmoothInterpolation = this.getInterpolation() === InterpolateSmooth,\n\n\t\t\t\twriteIndex = 1,\n\t\t\t\tlastIndex = times.length - 1;\n\n\t\t\tfor( var i = 1; i < lastIndex; ++ i ) {\n\n\t\t\t\tvar keep = false;\n\n\t\t\t\tvar time = times[ i ];\n\t\t\t\tvar timeNext = times[ i + 1 ];\n\n\t\t\t\t// remove adjacent keyframes scheduled at the same time\n\n\t\t\t\tif ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {\n\n\t\t\t\t\tif ( ! smoothInterpolation ) {\n\n\t\t\t\t\t\t// remove unnecessary keyframes same as their neighbors\n\n\t\t\t\t\t\tvar offset = i * stride,\n\t\t\t\t\t\t\toffsetP = offset - stride,\n\t\t\t\t\t\t\toffsetN = offset + stride;\n\n\t\t\t\t\t\tfor ( var j = 0; j !== stride; ++ j ) {\n\n\t\t\t\t\t\t\tvar value = values[ offset + j ];\n\n\t\t\t\t\t\t\tif ( value !== values[ offsetP + j ] ||\n\t\t\t\t\t\t\t\t\tvalue !== values[ offsetN + j ] ) {\n\n\t\t\t\t\t\t\t\tkeep = true;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else keep = true;\n\n\t\t\t\t}\n\n\t\t\t\t// in-place compaction\n\n\t\t\t\tif ( keep ) {\n\n\t\t\t\t\tif ( i !== writeIndex ) {\n\n\t\t\t\t\t\ttimes[ writeIndex ] = times[ i ];\n\n\t\t\t\t\t\tvar readOffset = i * stride,\n\t\t\t\t\t\t\twriteOffset = writeIndex * stride;\n\n\t\t\t\t\t\tfor ( var j = 0; j !== stride; ++ j )\n\n\t\t\t\t\t\t\tvalues[ writeOffset + j ] = values[ readOffset + j ];\n\n\t\t\t\t\t}\n\n\t\t\t\t\t++ writeIndex;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// flush last keyframe (compaction looks ahead)\n\n\t\t\tif ( lastIndex > 0 ) {\n\n\t\t\t\ttimes[ writeIndex ] = times[ lastIndex ];\n\n\t\t\t\tfor ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j )\n\n\t\t\t\t\tvalues[ writeOffset + j ] = values[ readOffset + j ];\n\n\t\t\t\t++ writeIndex;\n\n\t\t\t}\n\n\t\t\tif ( writeIndex !== times.length ) {\n\n\t\t\t\tthis.times = AnimationUtils.arraySlice( times, 0, writeIndex );\n\t\t\t\tthis.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t};\n\n\tfunction KeyframeTrackConstructor( name, times, values, interpolation ) {\n\n\t\tif( name === undefined ) throw new Error( \"track name is undefined\" );\n\n\t\tif( times === undefined || times.length === 0 ) {\n\n\t\t\tthrow new Error( \"no keyframes in track named \" + name );\n\n\t\t}\n\n\t\tthis.name = name;\n\n\t\tthis.times = AnimationUtils.convertArray( times, this.TimeBufferType );\n\t\tthis.values = AnimationUtils.convertArray( values, this.ValueBufferType );\n\n\t\tthis.setInterpolation( interpolation || this.DefaultInterpolation );\n\n\t\tthis.validate();\n\t\tthis.optimize();\n\n\t}\n\n\t/**\n\t *\n\t * A Track of vectored keyframe values.\n\t *\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t * @author tschw\n\t */\n\n\tfunction VectorKeyframeTrack( name, times, values, interpolation ) {\n\n\t\tKeyframeTrackConstructor.call( this, name, times, values, interpolation );\n\n\t}\n\n\tVectorKeyframeTrack.prototype =\n\t\t\tObject.assign( Object.create( KeyframeTrackPrototype ), {\n\n\t\tconstructor: VectorKeyframeTrack,\n\n\t\tValueTypeName: 'vector'\n\n\t\t// ValueBufferType is inherited\n\n\t\t// DefaultInterpolation is inherited\n\n\t} );\n\n\t/**\n\t * Spherical linear unit quaternion interpolant.\n\t *\n\t * @author tschw\n\t */\n\n\tfunction QuaternionLinearInterpolant(\n\t\t\tparameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tInterpolant.call(\n\t\t\t\tthis, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t}\n\n\tQuaternionLinearInterpolant.prototype =\n\t\t\tObject.assign( Object.create( Interpolant.prototype ), {\n\n\t\tconstructor: QuaternionLinearInterpolant,\n\n\t\tinterpolate_: function( i1, t0, t, t1 ) {\n\n\t\t\tvar result = this.resultBuffer,\n\t\t\t\tvalues = this.sampleValues,\n\t\t\t\tstride = this.valueSize,\n\n\t\t\t\toffset = i1 * stride,\n\n\t\t\t\talpha = ( t - t0 ) / ( t1 - t0 );\n\n\t\t\tfor ( var end = offset + stride; offset !== end; offset += 4 ) {\n\n\t\t\t\tQuaternion.slerpFlat( result, 0,\n\t\t\t\t\t\tvalues, offset - stride, values, offset, alpha );\n\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t *\n\t * A Track of quaternion keyframe values.\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t * @author tschw\n\t */\n\n\tfunction QuaternionKeyframeTrack( name, times, values, interpolation ) {\n\n\t\tKeyframeTrackConstructor.call( this, name, times, values, interpolation );\n\n\t}\n\n\tQuaternionKeyframeTrack.prototype =\n\t\t\tObject.assign( Object.create( KeyframeTrackPrototype ), {\n\n\t\tconstructor: QuaternionKeyframeTrack,\n\n\t\tValueTypeName: 'quaternion',\n\n\t\t// ValueBufferType is inherited\n\n\t\tDefaultInterpolation: InterpolateLinear,\n\n\t\tInterpolantFactoryMethodLinear: function( result ) {\n\n\t\t\treturn new QuaternionLinearInterpolant(\n\t\t\t\t\tthis.times, this.values, this.getValueSize(), result );\n\n\t\t},\n\n\t\tInterpolantFactoryMethodSmooth: undefined // not yet implemented\n\n\t} );\n\n\t/**\n\t *\n\t * A Track of numeric keyframe values.\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t * @author tschw\n\t */\n\n\tfunction NumberKeyframeTrack( name, times, values, interpolation ) {\n\n\t\tKeyframeTrackConstructor.call( this, name, times, values, interpolation );\n\n\t}\n\n\tNumberKeyframeTrack.prototype =\n\t\t\tObject.assign( Object.create( KeyframeTrackPrototype ), {\n\n\t\tconstructor: NumberKeyframeTrack,\n\n\t\tValueTypeName: 'number',\n\n\t\t// ValueBufferType is inherited\n\n\t\t// DefaultInterpolation is inherited\n\n\t} );\n\n\t/**\n\t *\n\t * A Track that interpolates Strings\n\t *\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t * @author tschw\n\t */\n\n\tfunction StringKeyframeTrack( name, times, values, interpolation ) {\n\n\t\tKeyframeTrackConstructor.call( this, name, times, values, interpolation );\n\n\t}\n\n\tStringKeyframeTrack.prototype =\n\t\t\tObject.assign( Object.create( KeyframeTrackPrototype ), {\n\n\t\tconstructor: StringKeyframeTrack,\n\n\t\tValueTypeName: 'string',\n\t\tValueBufferType: Array,\n\n\t\tDefaultInterpolation: InterpolateDiscrete,\n\n\t\tInterpolantFactoryMethodLinear: undefined,\n\n\t\tInterpolantFactoryMethodSmooth: undefined\n\n\t} );\n\n\t/**\n\t *\n\t * A Track of Boolean keyframe values.\n\t *\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t * @author tschw\n\t */\n\n\tfunction BooleanKeyframeTrack( name, times, values ) {\n\n\t\tKeyframeTrackConstructor.call( this, name, times, values );\n\n\t}\n\n\tBooleanKeyframeTrack.prototype =\n\t\t\tObject.assign( Object.create( KeyframeTrackPrototype ), {\n\n\t\tconstructor: BooleanKeyframeTrack,\n\n\t\tValueTypeName: 'bool',\n\t\tValueBufferType: Array,\n\n\t\tDefaultInterpolation: InterpolateDiscrete,\n\n\t\tInterpolantFactoryMethodLinear: undefined,\n\t\tInterpolantFactoryMethodSmooth: undefined\n\n\t\t// Note: Actually this track could have a optimized / compressed\n\t\t// representation of a single value and a custom interpolant that\n\t\t// computes \"firstValue ^ isOdd( index )\".\n\n\t} );\n\n\t/**\n\t *\n\t * A Track of keyframe values that represent color.\n\t *\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t * @author tschw\n\t */\n\n\tfunction ColorKeyframeTrack( name, times, values, interpolation ) {\n\n\t\tKeyframeTrackConstructor.call( this, name, times, values, interpolation );\n\n\t}\n\n\tColorKeyframeTrack.prototype =\n\t\t\tObject.assign( Object.create( KeyframeTrackPrototype ), {\n\n\t\tconstructor: ColorKeyframeTrack,\n\n\t\tValueTypeName: 'color'\n\n\t\t// ValueBufferType is inherited\n\n\t\t// DefaultInterpolation is inherited\n\n\n\t\t// Note: Very basic implementation and nothing special yet.\n\t\t// However, this is the place for color space parameterization.\n\n\t} );\n\n\t/**\n\t *\n\t * A timed sequence of keyframes for a specific property.\n\t *\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t * @author tschw\n\t */\n\n\tfunction KeyframeTrack( name, times, values, interpolation ) {\n\n\t\tKeyframeTrackConstructor.apply( this, arguments );\n\n\t}\n\n\tKeyframeTrack.prototype = KeyframeTrackPrototype;\n\tKeyframeTrackPrototype.constructor = KeyframeTrack;\n\n\t// Static methods:\n\n\tObject.assign( KeyframeTrack, {\n\n\t\t// Serialization (in static context, because of constructor invocation\n\t\t// and automatic invocation of .toJSON):\n\n\t\tparse: function( json ) {\n\n\t\t\tif( json.type === undefined ) {\n\n\t\t\t\tthrow new Error( \"track type undefined, can not parse\" );\n\n\t\t\t}\n\n\t\t\tvar trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );\n\n\t\t\tif ( json.times === undefined ) {\n\n\t\t\t\tvar times = [], values = [];\n\n\t\t\t\tAnimationUtils.flattenJSON( json.keys, times, values, 'value' );\n\n\t\t\t\tjson.times = times;\n\t\t\t\tjson.values = values;\n\n\t\t\t}\n\n\t\t\t// derived classes can define a static parse method\n\t\t\tif ( trackType.parse !== undefined ) {\n\n\t\t\t\treturn trackType.parse( json );\n\n\t\t\t} else {\n\n\t\t\t\t// by default, we asssume a constructor compatible with the base\n\t\t\t\treturn new trackType(\n\t\t\t\t\t\tjson.name, json.times, json.values, json.interpolation );\n\n\t\t\t}\n\n\t\t},\n\n\t\ttoJSON: function( track ) {\n\n\t\t\tvar trackType = track.constructor;\n\n\t\t\tvar json;\n\n\t\t\t// derived classes can define a static toJSON method\n\t\t\tif ( trackType.toJSON !== undefined ) {\n\n\t\t\t\tjson = trackType.toJSON( track );\n\n\t\t\t} else {\n\n\t\t\t\t// by default, we assume the data can be serialized as-is\n\t\t\t\tjson = {\n\n\t\t\t\t\t'name': track.name,\n\t\t\t\t\t'times': AnimationUtils.convertArray( track.times, Array ),\n\t\t\t\t\t'values': AnimationUtils.convertArray( track.values, Array )\n\n\t\t\t\t};\n\n\t\t\t\tvar interpolation = track.getInterpolation();\n\n\t\t\t\tif ( interpolation !== track.DefaultInterpolation ) {\n\n\t\t\t\t\tjson.interpolation = interpolation;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tjson.type = track.ValueTypeName; // mandatory\n\n\t\t\treturn json;\n\n\t\t},\n\n\t\t_getTrackTypeForValueTypeName: function( typeName ) {\n\n\t\t\tswitch( typeName.toLowerCase() ) {\n\n\t\t\t\tcase \"scalar\":\n\t\t\t\tcase \"double\":\n\t\t\t\tcase \"float\":\n\t\t\t\tcase \"number\":\n\t\t\t\tcase \"integer\":\n\n\t\t\t\t\treturn NumberKeyframeTrack;\n\n\t\t\t\tcase \"vector\":\n\t\t\t\tcase \"vector2\":\n\t\t\t\tcase \"vector3\":\n\t\t\t\tcase \"vector4\":\n\n\t\t\t\t\treturn VectorKeyframeTrack;\n\n\t\t\t\tcase \"color\":\n\n\t\t\t\t\treturn ColorKeyframeTrack;\n\n\t\t\t\tcase \"quaternion\":\n\n\t\t\t\t\treturn QuaternionKeyframeTrack;\n\n\t\t\t\tcase \"bool\":\n\t\t\t\tcase \"boolean\":\n\n\t\t\t\t\treturn BooleanKeyframeTrack;\n\n\t\t\t\tcase \"string\":\n\n\t\t\t\t\treturn StringKeyframeTrack;\n\n\t\t\t}\n\n\t\t\tthrow new Error( \"Unsupported typeName: \" + typeName );\n\n\t\t}\n\n\t} );\n\n\t/**\n\t *\n\t * Reusable set of Tracks that represent an animation.\n\t *\n\t * @author Ben Houston / http://clara.io/\n\t * @author David Sarno / http://lighthaus.us/\n\t */\n\n\tfunction AnimationClip( name, duration, tracks ) {\n\n\t\tthis.name = name;\n\t\tthis.tracks = tracks;\n\t\tthis.duration = ( duration !== undefined ) ? duration : -1;\n\n\t\tthis.uuid = _Math.generateUUID();\n\n\t\t// this means it should figure out its duration by scanning the tracks\n\t\tif ( this.duration < 0 ) {\n\n\t\t\tthis.resetDuration();\n\n\t\t}\n\n\t\tthis.optimize();\n\n\t}\n\n\tAnimationClip.prototype = {\n\n\t\tconstructor: AnimationClip,\n\n\t\tresetDuration: function() {\n\n\t\t\tvar tracks = this.tracks,\n\t\t\t\tduration = 0;\n\n\t\t\tfor ( var i = 0, n = tracks.length; i !== n; ++ i ) {\n\n\t\t\t\tvar track = this.tracks[ i ];\n\n\t\t\t\tduration = Math.max(\n\t\t\t\t\t\tduration, track.times[ track.times.length - 1 ] );\n\n\t\t\t}\n\n\t\t\tthis.duration = duration;\n\n\t\t},\n\n\t\ttrim: function() {\n\n\t\t\tfor ( var i = 0; i < this.tracks.length; i ++ ) {\n\n\t\t\t\tthis.tracks[ i ].trim( 0, this.duration );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\toptimize: function() {\n\n\t\t\tfor ( var i = 0; i < this.tracks.length; i ++ ) {\n\n\t\t\t\tthis.tracks[ i ].optimize();\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t};\n\n\t// Static methods:\n\n\tObject.assign( AnimationClip, {\n\n\t\tparse: function( json ) {\n\n\t\t\tvar tracks = [],\n\t\t\t\tjsonTracks = json.tracks,\n\t\t\t\tframeTime = 1.0 / ( json.fps || 1.0 );\n\n\t\t\tfor ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) {\n\n\t\t\t\ttracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) );\n\n\t\t\t}\n\n\t\t\treturn new AnimationClip( json.name, json.duration, tracks );\n\n\t\t},\n\n\n\t\ttoJSON: function( clip ) {\n\n\t\t\tvar tracks = [],\n\t\t\t\tclipTracks = clip.tracks;\n\n\t\t\tvar json = {\n\n\t\t\t\t'name': clip.name,\n\t\t\t\t'duration': clip.duration,\n\t\t\t\t'tracks': tracks\n\n\t\t\t};\n\n\t\t\tfor ( var i = 0, n = clipTracks.length; i !== n; ++ i ) {\n\n\t\t\t\ttracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );\n\n\t\t\t}\n\n\t\t\treturn json;\n\n\t\t},\n\n\n\t\tCreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) {\n\n\t\t\tvar numMorphTargets = morphTargetSequence.length;\n\t\t\tvar tracks = [];\n\n\t\t\tfor ( var i = 0; i < numMorphTargets; i ++ ) {\n\n\t\t\t\tvar times = [];\n\t\t\t\tvar values = [];\n\n\t\t\t\ttimes.push(\n\t\t\t\t\t\t( i + numMorphTargets - 1 ) % numMorphTargets,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\t( i + 1 ) % numMorphTargets );\n\n\t\t\t\tvalues.push( 0, 1, 0 );\n\n\t\t\t\tvar order = AnimationUtils.getKeyframeOrder( times );\n\t\t\t\ttimes = AnimationUtils.sortedArray( times, 1, order );\n\t\t\t\tvalues = AnimationUtils.sortedArray( values, 1, order );\n\n\t\t\t\t// if there is a key at the first frame, duplicate it as the\n\t\t\t\t// last frame as well for perfect loop.\n\t\t\t\tif ( ! noLoop && times[ 0 ] === 0 ) {\n\n\t\t\t\t\ttimes.push( numMorphTargets );\n\t\t\t\t\tvalues.push( values[ 0 ] );\n\n\t\t\t\t}\n\n\t\t\t\ttracks.push(\n\t\t\t\t\t\tnew NumberKeyframeTrack(\n\t\t\t\t\t\t\t'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',\n\t\t\t\t\t\t\ttimes, values\n\t\t\t\t\t\t).scale( 1.0 / fps ) );\n\t\t\t}\n\n\t\t\treturn new AnimationClip( name, -1, tracks );\n\n\t\t},\n\n\t\tfindByName: function( objectOrClipArray, name ) {\n\n\t\t\tvar clipArray = objectOrClipArray;\n\n\t\t\tif ( ! Array.isArray( objectOrClipArray ) ) {\n\n\t\t\t\tvar o = objectOrClipArray;\n\t\t\t\tclipArray = o.geometry && o.geometry.animations || o.animations;\n\n\t\t\t}\n\n\t\t\tfor ( var i = 0; i < clipArray.length; i ++ ) {\n\n\t\t\t\tif ( clipArray[ i ].name === name ) {\n\n\t\t\t\t\treturn clipArray[ i ];\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\n\t\t},\n\n\t\tCreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) {\n\n\t\t\tvar animationToMorphTargets = {};\n\n\t\t\t// tested with https://regex101.com/ on trick sequences\n\t\t\t// such flamingo_flyA_003, flamingo_run1_003, crdeath0059\n\t\t\tvar pattern = /^([\\w-]*?)([\\d]+)$/;\n\n\t\t\t// sort morph target names into animation groups based\n\t\t\t// patterns like Walk_001, Walk_002, Run_001, Run_002\n\t\t\tfor ( var i = 0, il = morphTargets.length; i < il; i ++ ) {\n\n\t\t\t\tvar morphTarget = morphTargets[ i ];\n\t\t\t\tvar parts = morphTarget.name.match( pattern );\n\n\t\t\t\tif ( parts && parts.length > 1 ) {\n\n\t\t\t\t\tvar name = parts[ 1 ];\n\n\t\t\t\t\tvar animationMorphTargets = animationToMorphTargets[ name ];\n\t\t\t\t\tif ( ! animationMorphTargets ) {\n\n\t\t\t\t\t\tanimationToMorphTargets[ name ] = animationMorphTargets = [];\n\n\t\t\t\t\t}\n\n\t\t\t\t\tanimationMorphTargets.push( morphTarget );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar clips = [];\n\n\t\t\tfor ( var name in animationToMorphTargets ) {\n\n\t\t\t\tclips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );\n\n\t\t\t}\n\n\t\t\treturn clips;\n\n\t\t},\n\n\t\t// parse the animation.hierarchy format\n\t\tparseAnimation: function( animation, bones ) {\n\n\t\t\tif ( ! animation ) {\n\n\t\t\t\tconsole.error( \" no animation in JSONLoader data\" );\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t\tvar addNonemptyTrack = function(\n\t\t\t\t\ttrackType, trackName, animationKeys, propertyName, destTracks ) {\n\n\t\t\t\t// only return track if there are actually keys.\n\t\t\t\tif ( animationKeys.length !== 0 ) {\n\n\t\t\t\t\tvar times = [];\n\t\t\t\t\tvar values = [];\n\n\t\t\t\t\tAnimationUtils.flattenJSON(\n\t\t\t\t\t\t\tanimationKeys, times, values, propertyName );\n\n\t\t\t\t\t// empty keys are filtered out, so check again\n\t\t\t\t\tif ( times.length !== 0 ) {\n\n\t\t\t\t\t\tdestTracks.push( new trackType( trackName, times, values ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t\tvar tracks = [];\n\n\t\t\tvar clipName = animation.name || 'default';\n\t\t\t// automatic length determination in AnimationClip.\n\t\t\tvar duration = animation.length || -1;\n\t\t\tvar fps = animation.fps || 30;\n\n\t\t\tvar hierarchyTracks = animation.hierarchy || [];\n\n\t\t\tfor ( var h = 0; h < hierarchyTracks.length; h ++ ) {\n\n\t\t\t\tvar animationKeys = hierarchyTracks[ h ].keys;\n\n\t\t\t\t// skip empty tracks\n\t\t\t\tif ( ! animationKeys || animationKeys.length === 0 ) continue;\n\n\t\t\t\t// process morph targets in a way exactly compatible\n\t\t\t\t// with AnimationHandler.init( animation )\n\t\t\t\tif ( animationKeys[0].morphTargets ) {\n\n\t\t\t\t\t// figure out all morph targets used in this track\n\t\t\t\t\tvar morphTargetNames = {};\n\t\t\t\t\tfor ( var k = 0; k < animationKeys.length; k ++ ) {\n\n\t\t\t\t\t\tif ( animationKeys[k].morphTargets ) {\n\n\t\t\t\t\t\t\tfor ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {\n\n\t\t\t\t\t\t\t\tmorphTargetNames[ animationKeys[k].morphTargets[m] ] = -1;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// create a track for each morph target with all zero\n\t\t\t\t\t// morphTargetInfluences except for the keys in which\n\t\t\t\t\t// the morphTarget is named.\n\t\t\t\t\tfor ( var morphTargetName in morphTargetNames ) {\n\n\t\t\t\t\t\tvar times = [];\n\t\t\t\t\t\tvar values = [];\n\n\t\t\t\t\t\tfor ( var m = 0;\n\t\t\t\t\t\t\t\tm !== animationKeys[k].morphTargets.length; ++ m ) {\n\n\t\t\t\t\t\t\tvar animationKey = animationKeys[k];\n\n\t\t\t\t\t\t\ttimes.push( animationKey.time );\n\t\t\t\t\t\t\tvalues.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttracks.push( new NumberKeyframeTrack(\n\t\t\t\t\t\t\t\t'.morphTargetInfluence[' + morphTargetName + ']', times, values ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tduration = morphTargetNames.length * ( fps || 1.0 );\n\n\t\t\t\t} else {\n\t\t\t\t\t// ...assume skeletal animation\n\n\t\t\t\t\tvar boneName = '.bones[' + bones[ h ].name + ']';\n\n\t\t\t\t\taddNonemptyTrack(\n\t\t\t\t\t\t\tVectorKeyframeTrack, boneName + '.position',\n\t\t\t\t\t\t\tanimationKeys, 'pos', tracks );\n\n\t\t\t\t\taddNonemptyTrack(\n\t\t\t\t\t\t\tQuaternionKeyframeTrack, boneName + '.quaternion',\n\t\t\t\t\t\t\tanimationKeys, 'rot', tracks );\n\n\t\t\t\t\taddNonemptyTrack(\n\t\t\t\t\t\t\tVectorKeyframeTrack, boneName + '.scale',\n\t\t\t\t\t\t\tanimationKeys, 'scl', tracks );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( tracks.length === 0 ) {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t\tvar clip = new AnimationClip( clipName, duration, tracks );\n\n\t\t\treturn clip;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction MaterialLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\t\tthis.textures = {};\n\n\t}\n\n\tObject.assign( MaterialLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tvar scope = this;\n\n\t\t\tvar loader = new XHRLoader( scope.manager );\n\t\t\tloader.load( url, function ( text ) {\n\n\t\t\t\tonLoad( scope.parse( JSON.parse( text ) ) );\n\n\t\t\t}, onProgress, onError );\n\n\t\t},\n\n\t\tsetTextures: function ( value ) {\n\n\t\t\tthis.textures = value;\n\n\t\t},\n\n\t\tparse: function ( json ) {\n\n\t\t\tvar textures = this.textures;\n\n\t\t\tfunction getTexture( name ) {\n\n\t\t\t\tif ( textures[ name ] === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.MaterialLoader: Undefined texture', name );\n\n\t\t\t\t}\n\n\t\t\t\treturn textures[ name ];\n\n\t\t\t}\n\n\t\t\tvar material = new Materials[ json.type ]();\n\n\t\t\tif ( json.uuid !== undefined ) material.uuid = json.uuid;\n\t\t\tif ( json.name !== undefined ) material.name = json.name;\n\t\t\tif ( json.color !== undefined ) material.color.setHex( json.color );\n\t\t\tif ( json.roughness !== undefined ) material.roughness = json.roughness;\n\t\t\tif ( json.metalness !== undefined ) material.metalness = json.metalness;\n\t\t\tif ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );\n\t\t\tif ( json.specular !== undefined ) material.specular.setHex( json.specular );\n\t\t\tif ( json.shininess !== undefined ) material.shininess = json.shininess;\n\t\t\tif ( json.uniforms !== undefined ) material.uniforms = json.uniforms;\n\t\t\tif ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;\n\t\t\tif ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;\n\t\t\tif ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;\n\t\t\tif ( json.fog !== undefined ) material.fog = json.fog;\n\t\t\tif ( json.shading !== undefined ) material.shading = json.shading;\n\t\t\tif ( json.blending !== undefined ) material.blending = json.blending;\n\t\t\tif ( json.side !== undefined ) material.side = json.side;\n\t\t\tif ( json.opacity !== undefined ) material.opacity = json.opacity;\n\t\t\tif ( json.transparent !== undefined ) material.transparent = json.transparent;\n\t\t\tif ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;\n\t\t\tif ( json.depthTest !== undefined ) material.depthTest = json.depthTest;\n\t\t\tif ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;\n\t\t\tif ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;\n\t\t\tif ( json.wireframe !== undefined ) material.wireframe = json.wireframe;\n\t\t\tif ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;\n\t\t\tif ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;\n\t\t\tif ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;\n\t\t\tif ( json.skinning !== undefined ) material.skinning = json.skinning;\n\t\t\tif ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;\n\n\t\t\t// for PointsMaterial\n\n\t\t\tif ( json.size !== undefined ) material.size = json.size;\n\t\t\tif ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;\n\n\t\t\t// maps\n\n\t\t\tif ( json.map !== undefined ) material.map = getTexture( json.map );\n\n\t\t\tif ( json.alphaMap !== undefined ) {\n\n\t\t\t\tmaterial.alphaMap = getTexture( json.alphaMap );\n\t\t\t\tmaterial.transparent = true;\n\n\t\t\t}\n\n\t\t\tif ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );\n\t\t\tif ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;\n\n\t\t\tif ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );\n\t\t\tif ( json.normalScale !== undefined ) {\n\n\t\t\t\tvar normalScale = json.normalScale;\n\n\t\t\t\tif ( Array.isArray( normalScale ) === false ) {\n\n\t\t\t\t\t// Blender exporter used to export a scalar. See #7459\n\n\t\t\t\t\tnormalScale = [ normalScale, normalScale ];\n\n\t\t\t\t}\n\n\t\t\t\tmaterial.normalScale = new Vector2().fromArray( normalScale );\n\n\t\t\t}\n\n\t\t\tif ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );\n\t\t\tif ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;\n\t\t\tif ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;\n\n\t\t\tif ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );\n\t\t\tif ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );\n\n\t\t\tif ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );\n\t\t\tif ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;\n\n\t\t\tif ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );\n\n\t\t\tif ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );\n\n\t\t\tif ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;\n\n\t\t\tif ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );\n\t\t\tif ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;\n\n\t\t\tif ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );\n\t\t\tif ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;\n\n\t\t\t// MultiMaterial\n\n\t\t\tif ( json.materials !== undefined ) {\n\n\t\t\t\tfor ( var i = 0, l = json.materials.length; i < l; i ++ ) {\n\n\t\t\t\t\tmaterial.materials.push( this.parse( json.materials[ i ] ) );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn material;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction BufferGeometryLoader( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t}\n\n\tObject.assign( BufferGeometryLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tvar scope = this;\n\n\t\t\tvar loader = new XHRLoader( scope.manager );\n\t\t\tloader.load( url, function ( text ) {\n\n\t\t\t\tonLoad( scope.parse( JSON.parse( text ) ) );\n\n\t\t\t}, onProgress, onError );\n\n\t\t},\n\n\t\tparse: function ( json ) {\n\n\t\t\tvar geometry = new BufferGeometry();\n\n\t\t\tvar index = json.data.index;\n\n\t\t\tvar TYPED_ARRAYS = {\n\t\t\t\t'Int8Array': Int8Array,\n\t\t\t\t'Uint8Array': Uint8Array,\n\t\t\t\t'Uint8ClampedArray': Uint8ClampedArray,\n\t\t\t\t'Int16Array': Int16Array,\n\t\t\t\t'Uint16Array': Uint16Array,\n\t\t\t\t'Int32Array': Int32Array,\n\t\t\t\t'Uint32Array': Uint32Array,\n\t\t\t\t'Float32Array': Float32Array,\n\t\t\t\t'Float64Array': Float64Array\n\t\t\t};\n\n\t\t\tif ( index !== undefined ) {\n\n\t\t\t\tvar typedArray = new TYPED_ARRAYS[ index.type ]( index.array );\n\t\t\t\tgeometry.setIndex( new BufferAttribute( typedArray, 1 ) );\n\n\t\t\t}\n\n\t\t\tvar attributes = json.data.attributes;\n\n\t\t\tfor ( var key in attributes ) {\n\n\t\t\t\tvar attribute = attributes[ key ];\n\t\t\t\tvar typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array );\n\n\t\t\t\tgeometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) );\n\n\t\t\t}\n\n\t\t\tvar groups = json.data.groups || json.data.drawcalls || json.data.offsets;\n\n\t\t\tif ( groups !== undefined ) {\n\n\t\t\t\tfor ( var i = 0, n = groups.length; i !== n; ++ i ) {\n\n\t\t\t\t\tvar group = groups[ i ];\n\n\t\t\t\t\tgeometry.addGroup( group.start, group.count, group.materialIndex );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tvar boundingSphere = json.data.boundingSphere;\n\n\t\t\tif ( boundingSphere !== undefined ) {\n\n\t\t\t\tvar center = new Vector3();\n\n\t\t\t\tif ( boundingSphere.center !== undefined ) {\n\n\t\t\t\t\tcenter.fromArray( boundingSphere.center );\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.boundingSphere = new Sphere( center, boundingSphere.radius );\n\n\t\t\t}\n\n\t\t\treturn geometry;\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction Loader() {\n\n\t\tthis.onLoadStart = function () {};\n\t\tthis.onLoadProgress = function () {};\n\t\tthis.onLoadComplete = function () {};\n\n\t}\n\n\tLoader.prototype = {\n\n\t\tconstructor: Loader,\n\n\t\tcrossOrigin: undefined,\n\n\t\textractUrlBase: function ( url ) {\n\n\t\t\tvar parts = url.split( '/' );\n\n\t\t\tif ( parts.length === 1 ) return './';\n\n\t\t\tparts.pop();\n\n\t\t\treturn parts.join( '/' ) + '/';\n\n\t\t},\n\n\t\tinitMaterials: function ( materials, texturePath, crossOrigin ) {\n\n\t\t\tvar array = [];\n\n\t\t\tfor ( var i = 0; i < materials.length; ++ i ) {\n\n\t\t\t\tarray[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );\n\n\t\t\t}\n\n\t\t\treturn array;\n\n\t\t},\n\n\t\tcreateMaterial: ( function () {\n\n\t\t\tvar color, textureLoader, materialLoader;\n\n\t\t\treturn function createMaterial( m, texturePath, crossOrigin ) {\n\n\t\t\t\tif ( color === undefined ) color = new Color();\n\t\t\t\tif ( textureLoader === undefined ) textureLoader = new TextureLoader();\n\t\t\t\tif ( materialLoader === undefined ) materialLoader = new MaterialLoader();\n\n\t\t\t\t// convert from old material format\n\n\t\t\t\tvar textures = {};\n\n\t\t\t\tfunction loadTexture( path, repeat, offset, wrap, anisotropy ) {\n\n\t\t\t\t\tvar fullPath = texturePath + path;\n\t\t\t\t\tvar loader = Loader.Handlers.get( fullPath );\n\n\t\t\t\t\tvar texture;\n\n\t\t\t\t\tif ( loader !== null ) {\n\n\t\t\t\t\t\ttexture = loader.load( fullPath );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ttextureLoader.setCrossOrigin( crossOrigin );\n\t\t\t\t\t\ttexture = textureLoader.load( fullPath );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( repeat !== undefined ) {\n\n\t\t\t\t\t\ttexture.repeat.fromArray( repeat );\n\n\t\t\t\t\t\tif ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping;\n\t\t\t\t\t\tif ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( offset !== undefined ) {\n\n\t\t\t\t\t\ttexture.offset.fromArray( offset );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( wrap !== undefined ) {\n\n\t\t\t\t\t\tif ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping;\n\t\t\t\t\t\tif ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping;\n\n\t\t\t\t\t\tif ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping;\n\t\t\t\t\t\tif ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( anisotropy !== undefined ) {\n\n\t\t\t\t\t\ttexture.anisotropy = anisotropy;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tvar uuid = _Math.generateUUID();\n\n\t\t\t\t\ttextures[ uuid ] = texture;\n\n\t\t\t\t\treturn uuid;\n\n\t\t\t\t}\n\n\t\t\t\t//\n\n\t\t\t\tvar json = {\n\t\t\t\t\tuuid: _Math.generateUUID(),\n\t\t\t\t\ttype: 'MeshLambertMaterial'\n\t\t\t\t};\n\n\t\t\t\tfor ( var name in m ) {\n\n\t\t\t\t\tvar value = m[ name ];\n\n\t\t\t\t\tswitch ( name ) {\n\t\t\t\t\t\tcase 'DbgColor':\n\t\t\t\t\t\tcase 'DbgIndex':\n\t\t\t\t\t\tcase 'opticalDensity':\n\t\t\t\t\t\tcase 'illumination':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'DbgName':\n\t\t\t\t\t\t\tjson.name = value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'blending':\n\t\t\t\t\t\t\tjson.blending = BlendingMode[ value ];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'colorAmbient':\n\t\t\t\t\t\tcase 'mapAmbient':\n\t\t\t\t\t\t\tconsole.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'colorDiffuse':\n\t\t\t\t\t\t\tjson.color = color.fromArray( value ).getHex();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'colorSpecular':\n\t\t\t\t\t\t\tjson.specular = color.fromArray( value ).getHex();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'colorEmissive':\n\t\t\t\t\t\t\tjson.emissive = color.fromArray( value ).getHex();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'specularCoef':\n\t\t\t\t\t\t\tjson.shininess = value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'shading':\n\t\t\t\t\t\t\tif ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';\n\t\t\t\t\t\t\tif ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';\n\t\t\t\t\t\t\tif ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapDiffuse':\n\t\t\t\t\t\t\tjson.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapDiffuseRepeat':\n\t\t\t\t\t\tcase 'mapDiffuseOffset':\n\t\t\t\t\t\tcase 'mapDiffuseWrap':\n\t\t\t\t\t\tcase 'mapDiffuseAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapEmissive':\n\t\t\t\t\t\t\tjson.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapEmissiveRepeat':\n\t\t\t\t\t\tcase 'mapEmissiveOffset':\n\t\t\t\t\t\tcase 'mapEmissiveWrap':\n\t\t\t\t\t\tcase 'mapEmissiveAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapLight':\n\t\t\t\t\t\t\tjson.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapLightRepeat':\n\t\t\t\t\t\tcase 'mapLightOffset':\n\t\t\t\t\t\tcase 'mapLightWrap':\n\t\t\t\t\t\tcase 'mapLightAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapAO':\n\t\t\t\t\t\t\tjson.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapAORepeat':\n\t\t\t\t\t\tcase 'mapAOOffset':\n\t\t\t\t\t\tcase 'mapAOWrap':\n\t\t\t\t\t\tcase 'mapAOAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapBump':\n\t\t\t\t\t\t\tjson.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapBumpScale':\n\t\t\t\t\t\t\tjson.bumpScale = value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapBumpRepeat':\n\t\t\t\t\t\tcase 'mapBumpOffset':\n\t\t\t\t\t\tcase 'mapBumpWrap':\n\t\t\t\t\t\tcase 'mapBumpAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapNormal':\n\t\t\t\t\t\t\tjson.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapNormalFactor':\n\t\t\t\t\t\t\tjson.normalScale = [ value, value ];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapNormalRepeat':\n\t\t\t\t\t\tcase 'mapNormalOffset':\n\t\t\t\t\t\tcase 'mapNormalWrap':\n\t\t\t\t\t\tcase 'mapNormalAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapSpecular':\n\t\t\t\t\t\t\tjson.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapSpecularRepeat':\n\t\t\t\t\t\tcase 'mapSpecularOffset':\n\t\t\t\t\t\tcase 'mapSpecularWrap':\n\t\t\t\t\t\tcase 'mapSpecularAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapMetalness':\n\t\t\t\t\t\t\tjson.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapMetalnessRepeat':\n\t\t\t\t\t\tcase 'mapMetalnessOffset':\n\t\t\t\t\t\tcase 'mapMetalnessWrap':\n\t\t\t\t\t\tcase 'mapMetalnessAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapRoughness':\n\t\t\t\t\t\t\tjson.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapRoughnessRepeat':\n\t\t\t\t\t\tcase 'mapRoughnessOffset':\n\t\t\t\t\t\tcase 'mapRoughnessWrap':\n\t\t\t\t\t\tcase 'mapRoughnessAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapAlpha':\n\t\t\t\t\t\t\tjson.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mapAlphaRepeat':\n\t\t\t\t\t\tcase 'mapAlphaOffset':\n\t\t\t\t\t\tcase 'mapAlphaWrap':\n\t\t\t\t\t\tcase 'mapAlphaAnisotropy':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'flipSided':\n\t\t\t\t\t\t\tjson.side = BackSide;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'doubleSided':\n\t\t\t\t\t\t\tjson.side = DoubleSide;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'transparency':\n\t\t\t\t\t\t\tconsole.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );\n\t\t\t\t\t\t\tjson.opacity = value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'depthTest':\n\t\t\t\t\t\tcase 'depthWrite':\n\t\t\t\t\t\tcase 'colorWrite':\n\t\t\t\t\t\tcase 'opacity':\n\t\t\t\t\t\tcase 'reflectivity':\n\t\t\t\t\t\tcase 'transparent':\n\t\t\t\t\t\tcase 'visible':\n\t\t\t\t\t\tcase 'wireframe':\n\t\t\t\t\t\t\tjson[ name ] = value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'vertexColors':\n\t\t\t\t\t\t\tif ( value === true ) json.vertexColors = VertexColors;\n\t\t\t\t\t\t\tif ( value === 'face' ) json.vertexColors = FaceColors;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tconsole.error( 'THREE.Loader.createMaterial: Unsupported', name, value );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( json.type === 'MeshBasicMaterial' ) delete json.emissive;\n\t\t\t\tif ( json.type !== 'MeshPhongMaterial' ) delete json.specular;\n\n\t\t\t\tif ( json.opacity < 1 ) json.transparent = true;\n\n\t\t\t\tmaterialLoader.setTextures( textures );\n\n\t\t\t\treturn materialLoader.parse( json );\n\n\t\t\t};\n\n\t\t} )()\n\n\t};\n\n\tLoader.Handlers = {\n\n\t\thandlers: [],\n\n\t\tadd: function ( regex, loader ) {\n\n\t\t\tthis.handlers.push( regex, loader );\n\n\t\t},\n\n\t\tget: function ( file ) {\n\n\t\t\tvar handlers = this.handlers;\n\n\t\t\tfor ( var i = 0, l = handlers.length; i < l; i += 2 ) {\n\n\t\t\t\tvar regex = handlers[ i ];\n\t\t\t\tvar loader = handlers[ i + 1 ];\n\n\t\t\t\tif ( regex.test( file ) ) {\n\n\t\t\t\t\treturn loader;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn null;\n\n\t\t}\n\n\t};\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t * @author alteredq / http://alteredqualia.com/\n\t */\n\n\tfunction JSONLoader( manager ) {\n\n\t\tif ( typeof manager === 'boolean' ) {\n\n\t\t\tconsole.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );\n\t\t\tmanager = undefined;\n\n\t\t}\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t\tthis.withCredentials = false;\n\n\t}\n\n\tObject.assign( JSONLoader.prototype, {\n\n\t\tload: function( url, onLoad, onProgress, onError ) {\n\n\t\t\tvar scope = this;\n\n\t\t\tvar texturePath = this.texturePath && ( typeof this.texturePath === \"string\" ) ? this.texturePath : Loader.prototype.extractUrlBase( url );\n\n\t\t\tvar loader = new XHRLoader( this.manager );\n\t\t\tloader.setWithCredentials( this.withCredentials );\n\t\t\tloader.load( url, function ( text ) {\n\n\t\t\t\tvar json = JSON.parse( text );\n\t\t\t\tvar metadata = json.metadata;\n\n\t\t\t\tif ( metadata !== undefined ) {\n\n\t\t\t\t\tvar type = metadata.type;\n\n\t\t\t\t\tif ( type !== undefined ) {\n\n\t\t\t\t\t\tif ( type.toLowerCase() === 'object' ) {\n\n\t\t\t\t\t\t\tconsole.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( type.toLowerCase() === 'scene' ) {\n\n\t\t\t\t\t\t\tconsole.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' );\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tvar object = scope.parse( json, texturePath );\n\t\t\t\tonLoad( object.geometry, object.materials );\n\n\t\t\t}, onProgress, onError );\n\n\t\t},\n\n\t\tsetTexturePath: function ( value ) {\n\n\t\t\tthis.texturePath = value;\n\n\t\t},\n\n\t\tparse: function ( json, texturePath ) {\n\n\t\t\tvar geometry = new Geometry(),\n\t\t\tscale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;\n\n\t\t\tparseModel( scale );\n\n\t\t\tparseSkin();\n\t\t\tparseMorphing( scale );\n\t\t\tparseAnimations();\n\n\t\t\tgeometry.computeFaceNormals();\n\t\t\tgeometry.computeBoundingSphere();\n\n\t\t\tfunction parseModel( scale ) {\n\n\t\t\t\tfunction isBitSet( value, position ) {\n\n\t\t\t\t\treturn value & ( 1 << position );\n\n\t\t\t\t}\n\n\t\t\t\tvar i, j, fi,\n\n\t\t\t\toffset, zLength,\n\n\t\t\tcolorIndex, normalIndex, uvIndex, materialIndex,\n\n\t\t\t\ttype,\n\t\t\t\tisQuad,\n\t\t\t\thasMaterial,\n\t\t\t\thasFaceVertexUv,\n\t\t\t\thasFaceNormal, hasFaceVertexNormal,\n\t\t\t\thasFaceColor, hasFaceVertexColor,\n\n\t\t\tvertex, face, faceA, faceB, hex, normal,\n\n\t\t\t\tuvLayer, uv, u, v,\n\n\t\t\t\tfaces = json.faces,\n\t\t\t\tvertices = json.vertices,\n\t\t\t\tnormals = json.normals,\n\t\t\t\tcolors = json.colors,\n\n\t\t\t\tnUvLayers = 0;\n\n\t\t\t\tif ( json.uvs !== undefined ) {\n\n\t\t\t\t\t// disregard empty arrays\n\n\t\t\t\t\tfor ( i = 0; i < json.uvs.length; i ++ ) {\n\n\t\t\t\t\t\tif ( json.uvs[ i ].length ) nUvLayers ++;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( i = 0; i < nUvLayers; i ++ ) {\n\n\t\t\t\t\t\tgeometry.faceVertexUvs[ i ] = [];\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\toffset = 0;\n\t\t\t\tzLength = vertices.length;\n\n\t\t\t\twhile ( offset < zLength ) {\n\n\t\t\t\t\tvertex = new Vector3();\n\n\t\t\t\t\tvertex.x = vertices[ offset ++ ] * scale;\n\t\t\t\t\tvertex.y = vertices[ offset ++ ] * scale;\n\t\t\t\t\tvertex.z = vertices[ offset ++ ] * scale;\n\n\t\t\t\t\tgeometry.vertices.push( vertex );\n\n\t\t\t\t}\n\n\t\t\t\toffset = 0;\n\t\t\t\tzLength = faces.length;\n\n\t\t\t\twhile ( offset < zLength ) {\n\n\t\t\t\t\ttype = faces[ offset ++ ];\n\n\n\t\t\t\t\tisQuad = isBitSet( type, 0 );\n\t\t\t\t\thasMaterial = isBitSet( type, 1 );\n\t\t\t\t\thasFaceVertexUv = isBitSet( type, 3 );\n\t\t\t\t\thasFaceNormal = isBitSet( type, 4 );\n\t\t\t\t\thasFaceVertexNormal = isBitSet( type, 5 );\n\t\t\t\t\thasFaceColor\t = isBitSet( type, 6 );\n\t\t\t\t\thasFaceVertexColor = isBitSet( type, 7 );\n\n\t\t\t\t\t// console.log(\"type\", type, \"bits\", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);\n\n\t\t\t\t\tif ( isQuad ) {\n\n\t\t\t\t\t\tfaceA = new Face3();\n\t\t\t\t\t\tfaceA.a = faces[ offset ];\n\t\t\t\t\t\tfaceA.b = faces[ offset + 1 ];\n\t\t\t\t\t\tfaceA.c = faces[ offset + 3 ];\n\n\t\t\t\t\t\tfaceB = new Face3();\n\t\t\t\t\t\tfaceB.a = faces[ offset + 1 ];\n\t\t\t\t\t\tfaceB.b = faces[ offset + 2 ];\n\t\t\t\t\t\tfaceB.c = faces[ offset + 3 ];\n\n\t\t\t\t\t\toffset += 4;\n\n\t\t\t\t\t\tif ( hasMaterial ) {\n\n\t\t\t\t\t\t\tmaterialIndex = faces[ offset ++ ];\n\t\t\t\t\t\t\tfaceA.materialIndex = materialIndex;\n\t\t\t\t\t\t\tfaceB.materialIndex = materialIndex;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// to get face <=> uv index correspondence\n\n\t\t\t\t\t\tfi = geometry.faces.length;\n\n\t\t\t\t\t\tif ( hasFaceVertexUv ) {\n\n\t\t\t\t\t\t\tfor ( i = 0; i < nUvLayers; i ++ ) {\n\n\t\t\t\t\t\t\t\tuvLayer = json.uvs[ i ];\n\n\t\t\t\t\t\t\t\tgeometry.faceVertexUvs[ i ][ fi ] = [];\n\t\t\t\t\t\t\t\tgeometry.faceVertexUvs[ i ][ fi + 1 ] = [];\n\n\t\t\t\t\t\t\t\tfor ( j = 0; j < 4; j ++ ) {\n\n\t\t\t\t\t\t\t\t\tuvIndex = faces[ offset ++ ];\n\n\t\t\t\t\t\t\t\t\tu = uvLayer[ uvIndex * 2 ];\n\t\t\t\t\t\t\t\t\tv = uvLayer[ uvIndex * 2 + 1 ];\n\n\t\t\t\t\t\t\t\t\tuv = new Vector2( u, v );\n\n\t\t\t\t\t\t\t\t\tif ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );\n\t\t\t\t\t\t\t\t\tif ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasFaceNormal ) {\n\n\t\t\t\t\t\t\tnormalIndex = faces[ offset ++ ] * 3;\n\n\t\t\t\t\t\t\tfaceA.normal.set(\n\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\tnormals[ normalIndex ]\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tfaceB.normal.copy( faceA.normal );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasFaceVertexNormal ) {\n\n\t\t\t\t\t\t\tfor ( i = 0; i < 4; i ++ ) {\n\n\t\t\t\t\t\t\t\tnormalIndex = faces[ offset ++ ] * 3;\n\n\t\t\t\t\t\t\t\tnormal = new Vector3(\n\t\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\t\tnormals[ normalIndex ]\n\t\t\t\t\t\t\t\t);\n\n\n\t\t\t\t\t\t\t\tif ( i !== 2 ) faceA.vertexNormals.push( normal );\n\t\t\t\t\t\t\t\tif ( i !== 0 ) faceB.vertexNormals.push( normal );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\n\t\t\t\t\t\tif ( hasFaceColor ) {\n\n\t\t\t\t\t\t\tcolorIndex = faces[ offset ++ ];\n\t\t\t\t\t\t\thex = colors[ colorIndex ];\n\n\t\t\t\t\t\t\tfaceA.color.setHex( hex );\n\t\t\t\t\t\t\tfaceB.color.setHex( hex );\n\n\t\t\t\t\t\t}\n\n\n\t\t\t\t\t\tif ( hasFaceVertexColor ) {\n\n\t\t\t\t\t\t\tfor ( i = 0; i < 4; i ++ ) {\n\n\t\t\t\t\t\t\t\tcolorIndex = faces[ offset ++ ];\n\t\t\t\t\t\t\t\thex = colors[ colorIndex ];\n\n\t\t\t\t\t\t\t\tif ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) );\n\t\t\t\t\t\t\t\tif ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgeometry.faces.push( faceA );\n\t\t\t\t\t\tgeometry.faces.push( faceB );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tface = new Face3();\n\t\t\t\t\t\tface.a = faces[ offset ++ ];\n\t\t\t\t\t\tface.b = faces[ offset ++ ];\n\t\t\t\t\t\tface.c = faces[ offset ++ ];\n\n\t\t\t\t\t\tif ( hasMaterial ) {\n\n\t\t\t\t\t\t\tmaterialIndex = faces[ offset ++ ];\n\t\t\t\t\t\t\tface.materialIndex = materialIndex;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// to get face <=> uv index correspondence\n\n\t\t\t\t\t\tfi = geometry.faces.length;\n\n\t\t\t\t\t\tif ( hasFaceVertexUv ) {\n\n\t\t\t\t\t\t\tfor ( i = 0; i < nUvLayers; i ++ ) {\n\n\t\t\t\t\t\t\t\tuvLayer = json.uvs[ i ];\n\n\t\t\t\t\t\t\t\tgeometry.faceVertexUvs[ i ][ fi ] = [];\n\n\t\t\t\t\t\t\t\tfor ( j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\t\t\t\t\tuvIndex = faces[ offset ++ ];\n\n\t\t\t\t\t\t\t\t\tu = uvLayer[ uvIndex * 2 ];\n\t\t\t\t\t\t\t\t\tv = uvLayer[ uvIndex * 2 + 1 ];\n\n\t\t\t\t\t\t\t\t\tuv = new Vector2( u, v );\n\n\t\t\t\t\t\t\t\t\tgeometry.faceVertexUvs[ i ][ fi ].push( uv );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasFaceNormal ) {\n\n\t\t\t\t\t\t\tnormalIndex = faces[ offset ++ ] * 3;\n\n\t\t\t\t\t\t\tface.normal.set(\n\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\tnormals[ normalIndex ]\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasFaceVertexNormal ) {\n\n\t\t\t\t\t\t\tfor ( i = 0; i < 3; i ++ ) {\n\n\t\t\t\t\t\t\t\tnormalIndex = faces[ offset ++ ] * 3;\n\n\t\t\t\t\t\t\t\tnormal = new Vector3(\n\t\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\t\tnormals[ normalIndex ++ ],\n\t\t\t\t\t\t\t\t\tnormals[ normalIndex ]\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tface.vertexNormals.push( normal );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\n\t\t\t\t\t\tif ( hasFaceColor ) {\n\n\t\t\t\t\t\t\tcolorIndex = faces[ offset ++ ];\n\t\t\t\t\t\t\tface.color.setHex( colors[ colorIndex ] );\n\n\t\t\t\t\t\t}\n\n\n\t\t\t\t\t\tif ( hasFaceVertexColor ) {\n\n\t\t\t\t\t\t\tfor ( i = 0; i < 3; i ++ ) {\n\n\t\t\t\t\t\t\t\tcolorIndex = faces[ offset ++ ];\n\t\t\t\t\t\t\t\tface.vertexColors.push( new Color( colors[ colorIndex ] ) );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgeometry.faces.push( face );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction parseSkin() {\n\n\t\t\t\tvar influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;\n\n\t\t\t\tif ( json.skinWeights ) {\n\n\t\t\t\t\tfor ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {\n\n\t\t\t\t\t\tvar x = json.skinWeights[ i ];\n\t\t\t\t\t\tvar y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;\n\t\t\t\t\t\tvar z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;\n\t\t\t\t\t\tvar w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;\n\n\t\t\t\t\t\tgeometry.skinWeights.push( new Vector4( x, y, z, w ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( json.skinIndices ) {\n\n\t\t\t\t\tfor ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {\n\n\t\t\t\t\t\tvar a = json.skinIndices[ i ];\n\t\t\t\t\t\tvar b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;\n\t\t\t\t\t\tvar c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;\n\t\t\t\t\t\tvar d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;\n\n\t\t\t\t\t\tgeometry.skinIndices.push( new Vector4( a, b, c, d ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.bones = json.bones;\n\n\t\t\t\tif ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {\n\n\t\t\t\t\tconsole.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +\n\t\t\t\t\t\tgeometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction parseMorphing( scale ) {\n\n\t\t\t\tif ( json.morphTargets !== undefined ) {\n\n\t\t\t\t\tfor ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {\n\n\t\t\t\t\t\tgeometry.morphTargets[ i ] = {};\n\t\t\t\t\t\tgeometry.morphTargets[ i ].name = json.morphTargets[ i ].name;\n\t\t\t\t\t\tgeometry.morphTargets[ i ].vertices = [];\n\n\t\t\t\t\t\tvar dstVertices = geometry.morphTargets[ i ].vertices;\n\t\t\t\t\t\tvar srcVertices = json.morphTargets[ i ].vertices;\n\n\t\t\t\t\t\tfor ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {\n\n\t\t\t\t\t\t\tvar vertex = new Vector3();\n\t\t\t\t\t\t\tvertex.x = srcVertices[ v ] * scale;\n\t\t\t\t\t\t\tvertex.y = srcVertices[ v + 1 ] * scale;\n\t\t\t\t\t\t\tvertex.z = srcVertices[ v + 2 ] * scale;\n\n\t\t\t\t\t\t\tdstVertices.push( vertex );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( json.morphColors !== undefined && json.morphColors.length > 0 ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.JSONLoader: \"morphColors\" no longer supported. Using them as face colors.' );\n\n\t\t\t\t\tvar faces = geometry.faces;\n\t\t\t\t\tvar morphColors = json.morphColors[ 0 ].colors;\n\n\t\t\t\t\tfor ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n\t\t\t\t\t\tfaces[ i ].color.fromArray( morphColors, i * 3 );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction parseAnimations() {\n\n\t\t\t\tvar outputAnimations = [];\n\n\t\t\t\t// parse old style Bone/Hierarchy animations\n\t\t\t\tvar animations = [];\n\n\t\t\t\tif ( json.animation !== undefined ) {\n\n\t\t\t\t\tanimations.push( json.animation );\n\n\t\t\t\t}\n\n\t\t\t\tif ( json.animations !== undefined ) {\n\n\t\t\t\t\tif ( json.animations.length ) {\n\n\t\t\t\t\t\tanimations = animations.concat( json.animations );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tanimations.push( json.animations );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tfor ( var i = 0; i < animations.length; i ++ ) {\n\n\t\t\t\t\tvar clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones );\n\t\t\t\t\tif ( clip ) outputAnimations.push( clip );\n\n\t\t\t\t}\n\n\t\t\t\t// parse implicit morph animations\n\t\t\t\tif ( geometry.morphTargets ) {\n\n\t\t\t\t\t// TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.\n\t\t\t\t\tvar morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );\n\t\t\t\t\toutputAnimations = outputAnimations.concat( morphAnimationClips );\n\n\t\t\t\t}\n\n\t\t\t\tif ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;\n\n\t\t\t}\n\n\t\t\tif ( json.materials === undefined || json.materials.length === 0 ) {\n\n\t\t\t\treturn { geometry: geometry };\n\n\t\t\t} else {\n\n\t\t\t\tvar materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin );\n\n\t\t\t\treturn { geometry: geometry, materials: materials };\n\n\t\t\t}\n\n\t\t}\n\n\t} );\n\n\t/**\n\t * @author mrdoob / http://mrdoob.com/\n\t */\n\n\tfunction ObjectLoader ( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\t\tthis.texturePath = '';\n\n\t}\n\n\tObject.assign( ObjectLoader.prototype, {\n\n\t\tload: function ( url, onLoad, onProgress, onError ) {\n\n\t\t\tif ( this.texturePath === '' ) {\n\n\t\t\t\tthis.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );\n\n\t\t\t}\n\n\t\t\tvar scope = this;\n\n\t\t\tvar loader = new XHRLoader( scope.manager );\n\t\t\tloader.load( url, function ( text ) {\n\n\t\t\t\tscope.parse( JSON.parse( text ), onLoad );\n\n\t\t\t}, onProgress, onError );\n\n\t\t},\n\n\t\tsetTexturePath: function ( value ) {\n\n\t\t\tthis.texturePath = value;\n\n\t\t},\n\n\t\tsetCrossOrigin: function ( value ) {\n\n\t\t\tthis.crossOrigin = value;\n\n\t\t},\n\n\t\tparse: function ( json, onLoad ) {\n\n\t\t\tvar geometries = this.parseGeometries( json.geometries );\n\n\t\t\tvar images = this.parseImages( json.images, function () {\n\n\t\t\t\tif ( onLoad !== undefined ) onLoad( object );\n\n\t\t\t} );\n\n\t\t\tvar textures = this.parseTextures( json.textures, images );\n\t\t\tvar materials = this.parseMaterials( json.materials, textures );\n\n\t\t\tvar object = this.parseObject( json.object, geometries, materials );\n\n\t\t\tif ( json.animations ) {\n\n\t\t\t\tobject.animations = this.parseAnimations( json.animations );\n\n\t\t\t}\n\n\t\t\tif ( json.images === undefined || json.images.length === 0 ) {\n\n\t\t\t\tif ( onLoad !== undefined ) onLoad( object );\n\n\t\t\t}\n\n\t\t\treturn object;\n\n\t\t},\n\n\t\tparseGeometries: function ( json ) {\n\n\t\t\tvar geometries = {};\n\n\t\t\tif ( json !== undefined ) {\n\n\t\t\t\tvar geometryLoader = new JSONLoader();\n\t\t\t\tvar bufferGeometryLoader = new BufferGeometryLoader();\n\n\t\t\t\tfor ( var i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\t\tvar geometry;\n\t\t\t\t\tvar data = json[ i ];\n\n\t\t\t\t\tswitch ( data.type ) {\n\n\t\t\t\t\t\tcase 'PlaneGeometry':\n\t\t\t\t\t\tcase 'PlaneBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.width,\n\t\t\t\t\t\t\t\tdata.height,\n\t\t\t\t\t\t\t\tdata.widthSegments,\n\t\t\t\t\t\t\t\tdata.heightSegments\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'BoxGeometry':\n\t\t\t\t\t\tcase 'BoxBufferGeometry':\n\t\t\t\t\t\tcase 'CubeGeometry': // backwards compatible\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.width,\n\t\t\t\t\t\t\t\tdata.height,\n\t\t\t\t\t\t\t\tdata.depth,\n\t\t\t\t\t\t\t\tdata.widthSegments,\n\t\t\t\t\t\t\t\tdata.heightSegments,\n\t\t\t\t\t\t\t\tdata.depthSegments\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'CircleGeometry':\n\t\t\t\t\t\tcase 'CircleBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.radius,\n\t\t\t\t\t\t\t\tdata.segments,\n\t\t\t\t\t\t\t\tdata.thetaStart,\n\t\t\t\t\t\t\t\tdata.thetaLength\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'CylinderGeometry':\n\t\t\t\t\t\tcase 'CylinderBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.radiusTop,\n\t\t\t\t\t\t\t\tdata.radiusBottom,\n\t\t\t\t\t\t\t\tdata.height,\n\t\t\t\t\t\t\t\tdata.radialSegments,\n\t\t\t\t\t\t\t\tdata.heightSegments,\n\t\t\t\t\t\t\t\tdata.openEnded,\n\t\t\t\t\t\t\t\tdata.thetaStart,\n\t\t\t\t\t\t\t\tdata.thetaLength\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'ConeGeometry':\n\t\t\t\t\t\tcase 'ConeBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.radius,\n\t\t\t\t\t\t\t\tdata.height,\n\t\t\t\t\t\t\t\tdata.radialSegments,\n\t\t\t\t\t\t\t\tdata.heightSegments,\n\t\t\t\t\t\t\t\tdata.openEnded,\n\t\t\t\t\t\t\t\tdata.thetaStart,\n\t\t\t\t\t\t\t\tdata.thetaLength\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'SphereGeometry':\n\t\t\t\t\t\tcase 'SphereBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.radius,\n\t\t\t\t\t\t\t\tdata.widthSegments,\n\t\t\t\t\t\t\t\tdata.heightSegments,\n\t\t\t\t\t\t\t\tdata.phiStart,\n\t\t\t\t\t\t\t\tdata.phiLength,\n\t\t\t\t\t\t\t\tdata.thetaStart,\n\t\t\t\t\t\t\t\tdata.thetaLength\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'DodecahedronGeometry':\n\t\t\t\t\t\tcase 'IcosahedronGeometry':\n\t\t\t\t\t\tcase 'OctahedronGeometry':\n\t\t\t\t\t\tcase 'TetrahedronGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.radius,\n\t\t\t\t\t\t\t\tdata.detail\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'RingGeometry':\n\t\t\t\t\t\tcase 'RingBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.innerRadius,\n\t\t\t\t\t\t\t\tdata.outerRadius,\n\t\t\t\t\t\t\t\tdata.thetaSegments,\n\t\t\t\t\t\t\t\tdata.phiSegments,\n\t\t\t\t\t\t\t\tdata.thetaStart,\n\t\t\t\t\t\t\t\tdata.thetaLength\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'TorusGeometry':\n\t\t\t\t\t\tcase 'TorusBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.radius,\n\t\t\t\t\t\t\t\tdata.tube,\n\t\t\t\t\t\t\t\tdata.radialSegments,\n\t\t\t\t\t\t\t\tdata.tubularSegments,\n\t\t\t\t\t\t\t\tdata.arc\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'TorusKnotGeometry':\n\t\t\t\t\t\tcase 'TorusKnotBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.radius,\n\t\t\t\t\t\t\t\tdata.tube,\n\t\t\t\t\t\t\t\tdata.tubularSegments,\n\t\t\t\t\t\t\t\tdata.radialSegments,\n\t\t\t\t\t\t\t\tdata.p,\n\t\t\t\t\t\t\t\tdata.q\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'LatheGeometry':\n\t\t\t\t\t\tcase 'LatheBufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = new Geometries[ data.type ](\n\t\t\t\t\t\t\t\tdata.points,\n\t\t\t\t\t\t\t\tdata.segments,\n\t\t\t\t\t\t\t\tdata.phiStart,\n\t\t\t\t\t\t\t\tdata.phiLength\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'BufferGeometry':\n\n\t\t\t\t\t\t\tgeometry = bufferGeometryLoader.parse( data );\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'Geometry':\n\n\t\t\t\t\t\t\tgeometry = geometryLoader.parse( data.data, this.texturePath ).geometry;\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Unsupported geometry type \"' + data.type + '\"' );\n\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tgeometry.uuid = data.uuid;\n\n\t\t\t\t\tif ( data.name !== undefined ) geometry.name = data.name;\n\n\t\t\t\t\tgeometries[ data.uuid ] = geometry;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn geometries;\n\n\t\t},\n\n\t\tparseMaterials: function ( json, textures ) {\n\n\t\t\tvar materials = {};\n\n\t\t\tif ( json !== undefined ) {\n\n\t\t\t\tvar loader = new MaterialLoader();\n\t\t\t\tloader.setTextures( textures );\n\n\t\t\t\tfor ( var i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\t\tvar material = loader.parse( json[ i ] );\n\t\t\t\t\tmaterials[ material.uuid ] = material;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn materials;\n\n\t\t},\n\n\t\tparseAnimations: function ( json ) {\n\n\t\t\tvar animations = [];\n\n\t\t\tfor ( var i = 0; i < json.length; i ++ ) {\n\n\t\t\t\tvar clip = AnimationClip.parse( json[ i ] );\n\n\t\t\t\tanimations.push( clip );\n\n\t\t\t}\n\n\t\t\treturn animations;\n\n\t\t},\n\n\t\tparseImages: function ( json, onLoad ) {\n\n\t\t\tvar scope = this;\n\t\t\tvar images = {};\n\n\t\t\tfunction loadImage( url ) {\n\n\t\t\t\tscope.manager.itemStart( url );\n\n\t\t\t\treturn loader.load( url, function () {\n\n\t\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t\t}, undefined, function () {\n\n\t\t\t\t\tscope.manager.itemError( url );\n\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\tif ( json !== undefined && json.length > 0 ) {\n\n\t\t\t\tvar manager = new LoadingManager( onLoad );\n\n\t\t\t\tvar loader = new ImageLoader( manager );\n\t\t\t\tloader.setCrossOrigin( this.crossOrigin );\n\n\t\t\t\tfor ( var i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\t\tvar image = json[ i ];\n\t\t\t\t\tvar path = /^(\\/\\/)|([a-z]+:(\\/\\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;\n\n\t\t\t\t\timages[ image.uuid ] = loadImage( path );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn images;\n\n\t\t},\n\n\t\tparseTextures: function ( json, images ) {\n\n\t\t\tfunction parseConstant( value, type ) {\n\n\t\t\t\tif ( typeof( value ) === 'number' ) return value;\n\n\t\t\t\tconsole.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );\n\n\t\t\t\treturn type[ value ];\n\n\t\t\t}\n\n\t\t\tvar textures = {};\n\n\t\t\tif ( json !== undefined ) {\n\n\t\t\t\tfor ( var i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\t\tvar data = json[ i ];\n\n\t\t\t\t\tif ( data.image === undefined ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: No \"image\" specified for', data.uuid );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( images[ data.image ] === undefined ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined image', data.image );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tvar texture = new Texture( images[ data.image ] );\n\t\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t\ttexture.uuid = data.uuid;\n\n\t\t\t\t\tif ( data.name !== undefined ) texture.name = data.name;\n\n\t\t\t\t\tif ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TextureMapping );\n\n\t\t\t\t\tif ( data.offset !== undefined ) texture.offset.fromArray( data.offset );\n\t\t\t\t\tif ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat );\n\t\t\t\t\tif ( data.wrap !== undefined ) {\n\n\t\t\t\t\t\ttexture.wrapS = parseConstant( data.wrap[ 0 ], TextureWrapping );\n\t\t\t\t\t\ttexture.wrapT = parseConstant( data.wrap[ 1 ], TextureWrapping );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TextureFilter );\n\t\t\t\t\tif ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TextureFilter );\n\t\t\t\t\tif ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;\n\n\t\t\t\t\tif ( data.flipY !== undefined ) texture.flipY = data.flipY;\n\n\t\t\t\t\ttextures[ data.uuid ] = texture;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn textures;\n\n\t\t},\n\n\t\tparseObject: function () {\n\n\t\t\tvar matrix = new Matrix4();\n\n\t\t\treturn function parseObject( data, geometries, materials ) {\n\n\t\t\t\tvar object;\n\n\t\t\t\tfunction getGeometry( name ) {\n\n\t\t\t\t\tif ( geometries[ name ] === undefined ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined geometry', name );\n\n\t\t\t\t\t}\n\n\t\t\t\t\treturn geometries[ name ];\n\n\t\t\t\t}\n\n\t\t\t\tfunction getMaterial( name ) {\n\n\t\t\t\t\tif ( name === undefined ) return undefined;\n\n\t\t\t\t\tif ( materials[ name ] === undefined ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined material', name );\n\n\t\t\t\t\t}\n\n\t\t\t\t\treturn materials[ name ];\n\n\t\t\t\t}\n\n\t\t\t\tswitch ( data.type ) {\n\n\t\t\t\t\tcase 'Scene':\n\n\t\t\t\t\t\tobject = new Scene();\n\n\t\t\t\t\t\tif ( data.background !== undefined ) {\n\n\t\t\t\t\t\t\tif ( Number.isInteger( data.background ) ) {\n\n\t\t\t\t\t\t\t\tobject.background = new Color( data.background );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( data.fog !== undefined ) {\n\n\t\t\t\t\t\t\tif ( data.fog.type === 'Fog' ) {\n\n\t\t\t\t\t\t\t\tobject.fog = new Fog( data.fog.color, data.fog.near, data.fog.far );\n\n\t\t\t\t\t\t\t} else if ( data.fog.type === 'FogExp2' ) {\n\n\t\t\t\t\t\t\t\tobject.fog = new FogExp2( data.fog.color, data.fog.density );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'PerspectiveCamera':\n\n\t\t\t\t\t\tobject = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far );\n\n\t\t\t\t\t\tif ( data.focus !== undefined ) object.focus = data.focus;\n\t\t\t\t\t\tif ( data.zoom !== undefined ) object.zoom = data.zoom;\n\t\t\t\t\t\tif ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge;\n\t\t\t\t\t\tif ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset;\n\t\t\t\t\t\tif ( data.view !== undefined ) object.view = Object.assign( {}, data.view );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'OrthographicCamera':\n\n\t\t\t\t\t\tobject = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'AmbientLight':\n\n\t\t\t\t\t\tobject = new AmbientLight( data.color, data.intensity );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'DirectionalLight':\n\n\t\t\t\t\t\tobject = new DirectionalLight( data.color, data.intensity );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'PointLight':\n\n\t\t\t\t\t\tobject = new PointLight( data.color, data.intensity, data.distance, data.decay );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'SpotLight':\n\n\t\t\t\t\t\tobject = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'HemisphereLight':\n\n\t\t\t\t\t\tobject = new HemisphereLight( data.color, data.groundColor, data.intensity );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'Mesh':\n\n\t\t\t\t\t\tvar geometry = getGeometry( data.geometry );\n\t\t\t\t\t\tvar material = getMaterial( data.material );\n\n\t\t\t\t\t\tif ( geometry.bones && geometry.bones.length > 0 ) {\n\n\t\t\t\t\t\t\tobject = new SkinnedMesh( geometry, material );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tobject = new Mesh( geometry, material );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'LOD':\n\n\t\t\t\t\t\tobject = new LOD();\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'Line':\n\n\t\t\t\t\t\tobject = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'LineSegments':\n\n\t\t\t\t\t\tobject = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'PointCloud':\n\t\t\t\t\tcase 'Points':\n\n\t\t\t\t\t\tobject = new Points( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'Sprite':\n\n\t\t\t\t\t\tobject = new Sprite( getMaterial( data.material ) );\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'Group':\n\n\t\t\t\t\t\tobject = new Group();\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\n\t\t\t\t\t\tobject = new Object3D();\n\n\t\t\t\t}\n\n\t\t\t\tobject.uuid = data.uuid;\n\n\t\t\t\tif ( data.name !== undefined ) object.name = data.name;\n\t\t\t\tif ( data.matrix !== undefined ) {\n\n\t\t\t\t\tmatrix.fromArray( data.matrix );\n\t\t\t\t\tmatrix.decompose( object.position, object.quaternion, object.scale );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( data.position !== undefined ) object.position.fromArray( data.position );\n\t\t\t\t\tif ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );\n\t\t\t\t\tif ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion );\n\t\t\t\t\tif ( data.scale !== undefined ) object.scale.fromArray( data.scale );\n\n\t\t\t\t}\n\n\t\t\t\tif ( data.castShadow !== undefined ) object.castShadow = data.castShadow;\n\t\t\t\tif ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;\n\n\t\t\t\tif ( data.shadow ) {\n\n\t\t\t\t\tif ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias;\n\t\t\t\t\tif ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius;\n\t\t\t\t\tif ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize );\n\t\t\t\t\tif ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera );\n\n\t\t\t\t}\n\n\t\t\t\tif ( data.visible !== undefined ) object.visible = data.visible;\n\t\t\t\tif ( data.userData !== undefined ) object.userData = data.userData;\n\n\t\t\t\tif ( data.children !== undefined ) {\n\n\t\t\t\t\tfor ( var child in data.children ) {\n\n\t\t\t\t\t\tobject.add( this.parseObject( data.children[ child ], geometries, materials ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( data.type === 'LOD' ) {\n\n\t\t\t\t\tvar levels = data.levels;\n\n\t\t\t\t\tfor ( var l = 0; l < levels.length; l ++ ) {\n\n\t\t\t\t\t\tvar level = levels[ l ];\n\t\t\t\t\t\tvar child = object.getObjectByProperty( 'uuid', level.object );\n\n\t\t\t\t\t\tif ( child !== undefined ) {\n\n\t\t\t\t\t\t\tobject.addLevel( child, level.distance );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn object;\n\n\t\t\t};\n\n\t\t}()\n\n\t} );\n\n\t/**\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t * Extensible curve object\n\t *\n\t * Some common of Curve methods\n\t * .getPoint(t), getTangent(t)\n\t * .getPointAt(u), getTangentAt(u)\n\t * .getPoints(), .getSpacedPoints()\n\t * .getLength()\n\t * .updateArcLengths()\n\t *\n\t * This following classes subclasses THREE.Curve:\n\t *\n\t * -- 2d classes --\n\t * THREE.LineCurve\n\t * THREE.QuadraticBezierCurve\n\t * THREE.CubicBezierCurve\n\t * THREE.SplineCurve\n\t * THREE.ArcCurve\n\t * THREE.EllipseCurve\n\t *\n\t * -- 3d classes --\n\t * THREE.LineCurve3\n\t * THREE.QuadraticBezierCurve3\n\t * THREE.CubicBezierCurve3\n\t * THREE.SplineCurve3\n\t *\n\t * A series of curves can be represented as a THREE.CurvePath\n\t *\n\t **/\n\n\t/**************************************************************\n\t *\tAbstract Curve base class\n\t **************************************************************/\n\n\tfunction Curve() {}\n\n\tCurve.prototype = {\n\n\t\tconstructor: Curve,\n\n\t\t// Virtual base class method to overwrite and implement in subclasses\n\t\t//\t- t [0 .. 1]\n\n\t\tgetPoint: function ( t ) {\n\n\t\t\tconsole.warn( \"THREE.Curve: Warning, getPoint() not implemented!\" );\n\t\t\treturn null;\n\n\t\t},\n\n\t\t// Get point at relative position in curve according to arc length\n\t\t// - u [0 .. 1]\n\n\t\tgetPointAt: function ( u ) {\n\n\t\t\tvar t = this.getUtoTmapping( u );\n\t\t\treturn this.getPoint( t );\n\n\t\t},\n\n\t\t// Get sequence of points using getPoint( t )\n\n\t\tgetPoints: function ( divisions ) {\n\n\t\t\tif ( ! divisions ) divisions = 5;\n\n\t\t\tvar points = [];\n\n\t\t\tfor ( var d = 0; d <= divisions; d ++ ) {\n\n\t\t\t\tpoints.push( this.getPoint( d / divisions ) );\n\n\t\t\t}\n\n\t\t\treturn points;\n\n\t\t},\n\n\t\t// Get sequence of points using getPointAt( u )\n\n\t\tgetSpacedPoints: function ( divisions ) {\n\n\t\t\tif ( ! divisions ) divisions = 5;\n\n\t\t\tvar points = [];\n\n\t\t\tfor ( var d = 0; d <= divisions; d ++ ) {\n\n\t\t\t\tpoints.push( this.getPointAt( d / divisions ) );\n\n\t\t\t}\n\n\t\t\treturn points;\n\n\t\t},\n\n\t\t// Get total curve arc length\n\n\t\tgetLength: function () {\n\n\t\t\tvar lengths = this.getLengths();\n\t\t\treturn lengths[ lengths.length - 1 ];\n\n\t\t},\n\n\t\t// Get list of cumulative segment lengths\n\n\t\tgetLengths: function ( divisions ) {\n\n\t\t\tif ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200;\n\n\t\t\tif ( this.cacheArcLengths\n\t\t\t\t&& ( this.cacheArcLengths.length === divisions + 1 )\n\t\t\t\t&& ! this.needsUpdate ) {\n\n\t\t\t\t//console.log( \"cached\", this.cacheArcLengths );\n\t\t\t\treturn this.cacheArcLengths;\n\n\t\t\t}\n\n\t\t\tthis.needsUpdate = false;\n\n\t\t\tvar cache = [];\n\t\t\tvar current, last = this.getPoint( 0 );\n\t\t\tvar p, sum = 0;\n\n\t\t\tcache.push( 0 );\n\n\t\t\tfor ( p = 1; p <= divisions; p ++ ) {\n\n\t\t\t\tcurrent = this.getPoint ( p / divisions );\n\t\t\t\tsum += current.distanceTo( last );\n\t\t\t\tcache.push( sum );\n\t\t\t\tlast = current;\n\n\t\t\t}\n\n\t\t\tthis.cacheArcLengths = cache;\n\n\t\t\treturn cache; // { sums: cache, sum:sum }; Sum is in the last element.\n\n\t\t},\n\n\t\tupdateArcLengths: function() {\n\n\t\t\tthis.needsUpdate = true;\n\t\t\tthis.getLengths();\n\n\t\t},\n\n\t\t// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant\n\n\t\tgetUtoTmapping: function ( u, distance ) {\n\n\t\t\tvar arcLengths = this.getLengths();\n\n\t\t\tvar i = 0, il = arcLengths.length;\n\n\t\t\tvar targetArcLength; // The targeted u distance value to get\n\n\t\t\tif ( distance ) {\n\n\t\t\t\ttargetArcLength = distance;\n\n\t\t\t} else {\n\n\t\t\t\ttargetArcLength = u * arcLengths[ il - 1 ];\n\n\t\t\t}\n\n\t\t\t//var time = Date.now();\n\n\t\t\t// binary search for the index with largest value smaller than target u distance\n\n\t\t\tvar low = 0, high = il - 1, comparison;\n\n\t\t\twhile ( low <= high ) {\n\n\t\t\t\ti = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats\n\n\t\t\t\tcomparison = arcLengths[ i ] - targetArcLength;\n\n\t\t\t\tif ( comparison < 0 ) {\n\n\t\t\t\t\tlow = i + 1;\n\n\t\t\t\t} else if ( comparison > 0 ) {\n\n\t\t\t\t\thigh = i - 1;\n\n\t\t\t\t} else {\n\n\t\t\t\t\thigh = i;\n\t\t\t\t\tbreak;\n\n\t\t\t\t\t// DONE\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\ti = high;\n\n\t\t\t//console.log('b' , i, low, high, Date.now()- time);\n\n\t\t\tif ( arcLengths[ i ] === targetArcLength ) {\n\n\t\t\t\tvar t = i / ( il - 1 );\n\t\t\t\treturn t;\n\n\t\t\t}\n\n\t\t\t// we could get finer grain at lengths, or use simple interpolation between two points\n\n\t\t\tvar lengthBefore = arcLengths[ i ];\n\t\t\tvar lengthAfter = arcLengths[ i + 1 ];\n\n\t\t\tvar segmentLength = lengthAfter - lengthBefore;\n\n\t\t\t// determine where we are between the 'before' and 'after' points\n\n\t\t\tvar segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;\n\n\t\t\t// add that fractional amount to t\n\n\t\t\tvar t = ( i + segmentFraction ) / ( il - 1 );\n\n\t\t\treturn t;\n\n\t\t},\n\n\t\t// Returns a unit vector tangent at t\n\t\t// In case any sub curve does not implement its tangent derivation,\n\t\t// 2 points a small delta apart will be used to find its gradient\n\t\t// which seems to give a reasonable approximation\n\n\t\tgetTangent: function( t ) {\n\n\t\t\tvar delta = 0.0001;\n\t\t\tvar t1 = t - delta;\n\t\t\tvar t2 = t + delta;\n\n\t\t\t// Capping in case of danger\n\n\t\t\tif ( t1 < 0 ) t1 = 0;\n\t\t\tif ( t2 > 1 ) t2 = 1;\n\n\t\t\tvar pt1 = this.getPoint( t1 );\n\t\t\tvar pt2 = this.getPoint( t2 );\n\n\t\t\tvar vec = pt2.clone().sub( pt1 );\n\t\t\treturn vec.normalize();\n\n\t\t},\n\n\t\tgetTangentAt: function ( u ) {\n\n\t\t\tvar t = this.getUtoTmapping( u );\n\t\t\treturn this.getTangent( t );\n\n\t\t},\n\n\t\tcomputeFrenetFrames: function ( segments, closed ) {\n\n\t\t\t// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf\n\n\t\t\tvar normal = new Vector3();\n\n\t\t\tvar tangents = [];\n\t\t\tvar normals = [];\n\t\t\tvar binormals = [];\n\n\t\t\tvar vec = new Vector3();\n\t\t\tvar mat = new Matrix4();\n\n\t\t\tvar i, u, theta;\n\n\t\t\t// compute the tangent vectors for each segment on the curve\n\n\t\t\tfor ( i = 0; i <= segments; i ++ ) {\n\n\t\t\t\tu = i / segments;\n\n\t\t\t\ttangents[ i ] = this.getTangentAt( u );\n\t\t\t\ttangents[ i ].normalize();\n\n\t\t\t}\n\n\t\t\t// select an initial normal vector perpendicular to the first tangent vector,\n\t\t\t// and in the direction of the minimum tangent xyz component\n\n\t\t\tnormals[ 0 ] = new Vector3();\n\t\t\tbinormals[ 0 ] = new Vector3();\n\t\t\tvar min = Number.MAX_VALUE;\n\t\t\tvar tx = Math.abs( tangents[ 0 ].x );\n\t\t\tvar ty = Math.abs( tangents[ 0 ].y );\n\t\t\tvar tz = Math.abs( tangents[ 0 ].z );\n\n\t\t\tif ( tx <= min ) {\n\n\t\t\t\tmin = tx;\n\t\t\t\tnormal.set( 1, 0, 0 );\n\n\t\t\t}\n\n\t\t\tif ( ty <= min ) {\n\n\t\t\t\tmin = ty;\n\t\t\t\tnormal.set( 0, 1, 0 );\n\n\t\t\t}\n\n\t\t\tif ( tz <= min ) {\n\n\t\t\t\tnormal.set( 0, 0, 1 );\n\n\t\t\t}\n\n\t\t\tvec.crossVectors( tangents[ 0 ], normal ).normalize();\n\n\t\t\tnormals[ 0 ].crossVectors( tangents[ 0 ], vec );\n\t\t\tbinormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );\n\n\n\t\t\t// compute the slowly-varying normal and binormal vectors for each segment on the curve\n\n\t\t\tfor ( i = 1; i <= segments; i ++ ) {\n\n\t\t\t\tnormals[ i ] = normals[ i - 1 ].clone();\n\n\t\t\t\tbinormals[ i ] = binormals[ i - 1 ].clone();\n\n\t\t\t\tvec.crossVectors( tangents[ i - 1 ], tangents[ i ] );\n\n\t\t\t\tif ( vec.length() > Number.EPSILON ) {\n\n\t\t\t\t\tvec.normalize();\n\n\t\t\t\t\ttheta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors\n\n\t\t\t\t\tnormals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );\n\n\t\t\t\t}\n\n\t\t\t\tbinormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n\t\t\t}\n\n\t\t\t// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same\n\n\t\t\tif ( closed === true ) {\n\n\t\t\t\ttheta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );\n\t\t\t\ttheta /= segments;\n\n\t\t\t\tif ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {\n\n\t\t\t\t\ttheta = - theta;\n\n\t\t\t\t}\n\n\t\t\t\tfor ( i = 1; i <= segments; i ++ ) {\n\n\t\t\t\t\t// twist a little...\n\t\t\t\t\tnormals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );\n\t\t\t\t\tbinormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttangents: tangents,\n\t\t\t\tnormals: normals,\n\t\t\t\tbinormals: binormals\n\t\t\t};\n\n\t\t}\n\n\t};\n\n\t// TODO: Transformation for Curves?\n\n\t/**************************************************************\n\t *\t3D Curves\n\t **************************************************************/\n\n\t// A Factory method for creating new curve subclasses\n\n\tCurve.create = function ( constructor, getPointFunc ) {\n\n\t\tconstructor.prototype = Object.create( Curve.prototype );\n\t\tconstructor.prototype.constructor = constructor;\n\t\tconstructor.prototype.getPoint = getPointFunc;\n\n\t\treturn constructor;\n\n\t};\n\n\t/**************************************************************\n\t *\tLine\n\t **************************************************************/\n\n\tfunction LineCurve( v1, v2 ) {\n\n\t\tthis.v1 = v1;\n\t\tthis.v2 = v2;\n\n\t}\n\n\tLineCurve.prototype = Object.create( Curve.prototype );\n\tLineCurve.prototype.constructor = LineCurve;\n\n\tLineCurve.prototype.isLineCurve = true;\n\n\tLineCurve.prototype.getPoint = function ( t ) {\n\n\t\tif ( t === 1 ) {\n\n\t\t\treturn this.v2.clone();\n\n\t\t}\n\n\t\tvar point = this.v2.clone().sub( this.v1 );\n\t\tpoint.multiplyScalar( t ).add( this.v1 );\n\n\t\treturn point;\n\n\t};\n\n\t// Line curve is linear, so we can overwrite default getPointAt\n\n\tLineCurve.prototype.getPointAt = function ( u ) {\n\n\t\treturn this.getPoint( u );\n\n\t};\n\n\tLineCurve.prototype.getTangent = function( t ) {\n\n\t\tvar tangent = this.v2.clone().sub( this.v1 );\n\n\t\treturn tangent.normalize();\n\n\t};\n\n\t/**\n\t * @author zz85 / http://www.lab4games.net/zz85/blog\n\t *\n\t **/\n\n\t/**************************************************************\n\t *\tCurved Path - a curve path is simply a array of connected\n\t * curves, but retains the api of a curve\n\t **************************************************************/\n\n\tfunction CurvePath() {\n\n\t\tthis.curves = [];\n\n\t\tthis.autoClose = false; // Automatically closes the path\n\n\t}\n\n\tCurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {\n\n\t\tconstructor: CurvePath,\n\n\t\tadd: function ( curve ) {\n\n\t\t\tthis.curves.push( curve );\n\n\t\t},\n\n\t\tclosePath: function () {\n\n\t\t\t// Add a line curve if start and end of lines are not connected\n\t\t\tvar startPoint = this.curves[ 0 ].getPoint( 0 );\n\t\t\tvar endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );\n\n\t\t\tif ( ! startPoint.equals( endPoint ) ) {\n\n\t\t\t\tthis.curves.push( new LineCurve( endPoint, startPoint ) );\n\n\t\t\t}\n\n\t\t},\n\n\t\t// To get accurate point with reference to\n\t\t// entire path distance at time t,\n\t\t// following has to be done:\n\n\t\t// 1. Length of each sub path have to be known\n\t\t// 2. Locate and identify type of curve\n\t\t// 3. Get t for the curve\n\t\t// 4. Return curve.getPointAt(t')\n\n\t\tgetPoint: function ( t ) {\n\n\t\t\tvar d = t * this.getLength();\n\t\t\tvar curveLengths = this.getCurveLengths();\n\t\t\tvar i = 0;\n\n\t\t\t// To think about boundaries points.\n\n\t\t\twhile ( i < curveLengths.length ) {\n\n\t\t\t\tif ( curveLengths[ i ] >= d ) {\n\n\t\t\t\t\tvar diff = curveLengths[ i ] - d;\n\t\t\t\t\tvar curve = this.curves[ i ];\n\n\t\t\t\t\tvar segmentLength = curve.getLength();\n\t\t\t\t\tvar u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;\n\n\t\t\t\t\treturn curve.getPointAt( u );\n\n\t\t\t\t}\n\n\t\t\t\ti ++;\n\n\t\t\t}\n\n\t\t\treturn null;\n\n\t\t\t// loop where sum != 0, sum > d , sum+1 <d\n\n\t\t},\n\n\t\t// We cannot use the default THREE.Curve getPoint() with getLength() because in\n\t\t// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath\n\t\t// getPoint() depends on getLength\n\n\t\tgetLength: function () {\n\n\t\t\tvar lens = this.getCurveLengths();\n\t\t\treturn lens[ lens.length - 1 ];\n\n\t\t},\n\n\t\t// cacheLengths must be recalculated.\n\t\tupdateArcLengths: function () {\n\n\t\t\tthis.needsUpdate = true;\n\t\t\tthis.cacheLengths = null;\n\t\t\tthis.getLengths();\n\n\t\t},\n\n\t\t// Compute lengths and cache them\n\t\t// We cannot overwrite getLengths() because UtoT mapping uses it.\n\n\t\tgetCurveLengths: function () {\n\n\t\t\t// We use cache values if curves and cache array are same length\n\n\t\t\tif ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {\n\n\t\t\t\treturn this.cacheLengths;\n\n\t\t\t}\n\n\t\t\t// Get length of sub-curve\n\t\t\t// Push sums into cached array\n\n\t\t\tvar lengths = [], sums = 0;\n\n\t\t\tfor ( var i = 0, l = this.curves.length; i < l; i ++ ) {\n\n\t\t\t\tsums += this.curves[ i ].getLength();\n\t\t\t\tlengths.push( sums );\n\n\t\t\t}\n\n\t\t\tthis.cacheLengths = lengths;\n\n\t\t\treturn lengths;\n\n\t\t},\n\n\t\tgetSpacedPoints: function ( divisions ) {\n\n\t\t\tif ( ! divisions ) divisions = 40;\n\n\t\t\tvar points = [];\n\n\t\t\tfor ( var i = 0; i <= divisions; i ++ ) {\n\n\t\t\t\tpoints.push( this.getPoint( i / divisions ) );\n\n\t\t\t}\n\n\t\t\tif ( this.autoClose ) {\n\n\t\t\t\tpoints.push( points[ 0 ] );\n\n\t\t\t}\n\n\t\t\treturn points;\n\n\t\t},\n\n\t\tgetPoints: function ( divisions ) {\n\n\t\t\tdivisions = divisions || 12;\n\n\t\t\tvar points = [], last;\n\n\t\t\tfor ( var i = 0, curves = this.curves; i < curves.length; i ++ ) {\n\n\t\t\t\tvar curve = curves[ i ];\n\t\t\t\tvar resolution = (curve && curve.isEllipseCurve) ? divisions * 2\n\t\t\t\t\t: (curve && curve.isLineCurve) ? 1\n\t\t\t\t\t: (curve && curve.isSplineCurve) ? divisions * curve.points.length\n\t\t\t\t\t: divisions;\n\n\t\t\t\tvar pts = curve.getPoints( resolution );\n\n\t\t\t\tfor ( var j = 0; j < pts.length; j++ ) {\n\n\t\t\t\t\tvar point = pts[ j ];\n\n\t\t\t\t\tif ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates\n\n\t\t\t\t\tpoints.push( point );\n\t\t\t\t\tlast = point;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( this.autoClose && points.length > 1 && !points[ points.length - 1 ].equals( points[ 0 ] ) ) {\n\n\t\t\t\tpoints.push( points[ 0 ] );\n\n\t\t\t}\n\n\t\t\treturn points;\n\n\t\t},\n\n\t\t/**************************************************************\n\t\t *\tCreate Geometries Helpers\n\t\t **************************************************************/\n\n\t\t/// Generate geometry from path points (for Line or Points objects)\n\n\t\tcreatePointsGeometry: function ( divisions ) {\n\n\t\t\tvar pts = this.getPoints( divisions );\n\t\t\treturn this.createGeometry( pts );\n\n\t\t},\n\n\t\t// Generate geometry from equidistant sampling along the path\n\n\t\tcreateSpacedPointsGeometry: function ( divisions ) {\n\n\t\t\tvar pts = this.getSpacedPoints( divisions );\n\t\t\treturn this.createGeometry( pts );\n\n\t\t},\n\n\t\tcreateGeometry: function ( points ) {\n\n\t\t\tvar geometry = new Geometry();\n\n\t\t\tfor ( var i = 0, l = points.length; i < l; i ++ ) {\n\n\t\t\t\tvar point = points[ i ];\n\t\t\t\tgeometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );\n\n\t\t\t}\n\n\t\t\treturn geometry;\n\n\t\t}\n\n\t} );\n\n\t/**************************************************************\n\t *\tEllipse curve\n\t **************************************************************/\n\n\tfunction EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n\t\tthis.aX = aX;\n\t\tthis.aY = aY;\n\n\t\tthis.xRadius = xRadius;\n\t\tthis.yRadius = yRadius;\n\n\t\tthis.aStartAngle = aStartAngle;\n\t\tthis.aEndAngle = aEndAngle;\n\n\t\tthis.aClockwise = aClockwise;\n\n\t\tthis.aRotation = aRotation || 0;\n\n\t}\n\n\tEllipseCurve.prototype = Object.create( Curve.prototype );\n\tEllipseCurve.prototype.constructor = EllipseCurve;\n\n\tEllipseCurve.prototype.isEllipseCurve = true;\n\n\tEllipseCurve.prototype.getPoint = function( t ) {\n\n\t\tvar twoPi = Math.PI * 2;\n\t\tvar deltaAngle = this.aEndAngle - this.aStartAngle;\n\t\tvar samePoints = Math.abs( deltaAngle ) < Number.EPSILON;\n\n\t\t// ensures that deltaAngle is 0 .. 2 PI\n\t\twhile ( deltaAngle < 0 ) deltaAngle += twoPi;\n\t\twhile ( deltaAngle > twoPi ) deltaAngle -= twoPi;\n\n\t\tif ( deltaAngle < Number.EPSILON ) {\n\n\t\t\tif ( samePoints ) {\n\n\t\t\t\tdeltaAngle = 0;\n\n\t\t\t} else {\n\n\t\t\t\tdeltaAngle = twoPi;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.aClockwise === true && ! samePoints
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment