Created
April 3, 2017 19:05
-
-
Save dkozma/4527ca9ad86f2650547d50ed068d63e1 to your computer and use it in GitHub Desktop.
This file has been truncated, but you can view the full file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******/ (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