text: var Three = (function (exports) {
  	'use strict';
  
  	var _Math = {
  
  		DEG2RAD: Math.PI / 180,
  		RAD2DEG: 180 / Math.PI,
  
  		generateUUID: ( function () {
  
  			// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  
  			var lut = [];
  
  			for ( var i = 0; i < 256; i ++ ) {
  
  				lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ).toUpperCase();
  
  			}
  
  			return function generateUUID() {
  
  				var d0 = Math.random() * 0xffffffff | 0;
  				var d1 = Math.random() * 0xffffffff | 0;
  				var d2 = Math.random() * 0xffffffff | 0;
  				var d3 = Math.random() * 0xffffffff | 0;
  				return lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
  					lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
  					lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
  					lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];
  
  			};
  
  		} )(),
  
  		clamp: function ( value, min, max ) {
  
  			return Math.max( min, Math.min( max, value ) );
  
  		},
  
  		// compute euclidian modulo of m % n
  		// https://en.wikipedia.org/wiki/Modulo_operation
  
  		euclideanModulo: function ( n, m ) {
  
  			return ( ( n % m ) + m ) % m;
  
  		},
  
  		// Linear mapping from range <a1, a2> to range <b1, b2>
  
  		mapLinear: function ( x, a1, a2, b1, b2 ) {
  
  			return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
  
  		},
  
  		// https://en.wikipedia.org/wiki/Linear_interpolation
  
  		lerp: function ( x, y, t ) {
  
  			return ( 1 - t ) * x + t * y;
  
  		},
  
  		// http://en.wikipedia.org/wiki/Smoothstep
  
  		smoothstep: function ( x, min, max ) {
  
  			if ( x <= min ) return 0;
  			if ( x >= max ) return 1;
  
  			x = ( x - min ) / ( max - min );
  
  			return x * x * ( 3 - 2 * x );
  
  		},
  
  		smootherstep: function ( x, min, max ) {
  
  			if ( x <= min ) return 0;
  			if ( x >= max ) return 1;
  
  			x = ( x - min ) / ( max - min );
  
  			return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
  
  		},
  
  		// Random integer from <low, high> interval
  
  		randInt: function ( low, high ) {
  
  			return low + Math.floor( Math.random() * ( high - low + 1 ) );
  
  		},
  
  		// Random float from <low, high> interval
  
  		randFloat: function ( low, high ) {
  
  			return low + Math.random() * ( high - low );
  
  		},
  
  		// Random float from <-range/2, range/2> interval
  
  		randFloatSpread: function ( range ) {
  
  			return range * ( 0.5 - Math.random() );
  
  		},
  
  		degToRad: function ( degrees ) {
  
  			return degrees * _Math.DEG2RAD;
  
  		},
  
  		radToDeg: function ( radians ) {
  
  			return radians * _Math.RAD2DEG;
  
  		},
  
  		isPowerOfTwo: function ( value ) {
  
  			return ( value & ( value - 1 ) ) === 0 && value !== 0;
  
  		},
  
  		ceilPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
  
  		},
  
  		floorPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
  
  		}
  
  	};
  
  	var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
  		'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
  		'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
  		'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
  		'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
  		'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
  		'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
  		'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
  		'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
  		'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
  		'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
  		'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
  		'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
  		'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
  		'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
  		'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
  		'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
  		'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
  		'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
  		'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
  		'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
  		'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
  		'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
  		'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
  
  	function Color( r, g, b ) {
  
  		if ( g === undefined && b === undefined ) {
  
  			// r is Color, hex or string
  			return this.set( r );
  
  		}
  
  		return this.setRGB( r, g, b );
  
  	}
  
  	Object.assign( Color.prototype, {
  
  		isColor: true,
  
  		r: 1, g: 1, b: 1,
  
  		set: function ( value ) {
  
  			if ( value && value.isColor ) {
  
  				this.copy( value );
  
  			} else if ( typeof value === 'number' ) {
  
  				this.setHex( value );
  
  			} else if ( typeof value === 'string' ) {
  
  				this.setStyle( value );
  
  			}
  
  			return this;
  
  		},
  
  		setScalar: function ( scalar ) {
  
  			this.r = scalar;
  			this.g = scalar;
  			this.b = scalar;
  
  			return this;
  
  		},
  
  		setHex: function ( hex ) {
  
  			hex = Math.floor( hex );
  
  			this.r = ( hex >> 16 & 255 ) / 255;
  			this.g = ( hex >> 8 & 255 ) / 255;
  			this.b = ( hex & 255 ) / 255;
  
  			return this;
  
  		},
  
  		setRGB: function ( r, g, b ) {
  
  			this.r = r;
  			this.g = g;
  			this.b = b;
  
  			return this;
  
  		},
  
  		setHSL: function () {
  
  			function hue2rgb( p, q, t ) {
  
  				if ( t < 0 ) t += 1;
  				if ( t > 1 ) t -= 1;
  				if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
  				if ( t < 1 / 2 ) return q;
  				if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
  				return p;
  
  			}
  
  			return function setHSL( h, s, l ) {
  
  				// h,s,l ranges are in 0.0 - 1.0
  				h = _Math.euclideanModulo( h, 1 );
  				s = _Math.clamp( s, 0, 1 );
  				l = _Math.clamp( l, 0, 1 );
  
  				if ( s === 0 ) {
  
  					this.r = this.g = this.b = l;
  
  				} else {
  
  					var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
  					var q = ( 2 * l ) - p;
  
  					this.r = hue2rgb( q, p, h + 1 / 3 );
  					this.g = hue2rgb( q, p, h );
  					this.b = hue2rgb( q, p, h - 1 / 3 );
  
  				}
  
  				return this;
  
  			};
  
  		}(),
  
  		setStyle: function ( style ) {
  
  			function handleAlpha( string ) {
  
  				if ( string === undefined ) return;
  
  				if ( parseFloat( string ) < 1 ) {
  
  					console.warn( 'Color: Alpha component of ' + style + ' will be ignored.' );
  
  				}
  
  			}
  
  
  			var m;
  
  			if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
  
  				// rgb / hsl
  
  				var color;
  				var name = m[ 1 ];
  				var components = m[ 2 ];
  
  				switch ( name ) {
  
  					case 'rgb':
  					case 'rgba':
  
  						if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
  
  							// rgb(255,0,0) rgba(255,0,0,0.5)
  							this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
  							this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
  							this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
  
  							handleAlpha( color[ 5 ] );
  
  							return this;
  
  						}
  
  						if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
  
  							// rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
  							this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
  							this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
  							this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
  
  							handleAlpha( color[ 5 ] );
  
  							return this;
  
  						}
  
  						break;
  
  					case 'hsl':
  					case 'hsla':
  
  						if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
  
  							// hsl(120,50%,50%) hsla(120,50%,50%,0.5)
  							var h = parseFloat( color[ 1 ] ) / 360;
  							var s = parseInt( color[ 2 ], 10 ) / 100;
  							var l = parseInt( color[ 3 ], 10 ) / 100;
  
  							handleAlpha( color[ 5 ] );
  
  							return this.setHSL( h, s, l );
  
  						}
  
  						break;
  
  				}
  
  			} else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {
  
  				// hex color
  
  				var hex = m[ 1 ];
  				var size = hex.length;
  
  				if ( size === 3 ) {
  
  					// #ff0
  					this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
  					this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
  					this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
  
  					return this;
  
  				} else if ( size === 6 ) {
  
  					// #ff0000
  					this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
  					this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
  					this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
  
  					return this;
  
  				}
  
  			}
  
  			if ( style && style.length > 0 ) {
  
  				// color keywords
  				var hex = ColorKeywords[ style ];
  
  				if ( hex !== undefined ) {
  
  					// red
  					this.setHex( hex );
  
  				} else {
  
  					// unknown color
  					console.warn( 'Color: Unknown color ' + style );
  
  				}
  
  			}
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this.r, this.g, this.b );
  
  		},
  
  		copy: function ( color ) {
  
  			this.r = color.r;
  			this.g = color.g;
  			this.b = color.b;
  
  			return this;
  
  		},
  
  		copyGammaToLinear: function ( color, gammaFactor ) {
  
  			if ( gammaFactor === undefined ) gammaFactor = 2.0;
  
  			this.r = Math.pow( color.r, gammaFactor );
  			this.g = Math.pow( color.g, gammaFactor );
  			this.b = Math.pow( color.b, gammaFactor );
  
  			return this;
  
  		},
  
  		copyLinearToGamma: function ( color, gammaFactor ) {
  
  			if ( gammaFactor === undefined ) gammaFactor = 2.0;
  
  			var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
  
  			this.r = Math.pow( color.r, safeInverse );
  			this.g = Math.pow( color.g, safeInverse );
  			this.b = Math.pow( color.b, safeInverse );
  
  			return this;
  
  		},
  
  		convertGammaToLinear: function () {
  
  			var r = this.r, g = this.g, b = this.b;
  
  			this.r = r * r;
  			this.g = g * g;
  			this.b = b * b;
  
  			return this;
  
  		},
  
  		convertLinearToGamma: function () {
  
  			this.r = Math.sqrt( this.r );
  			this.g = Math.sqrt( this.g );
  			this.b = Math.sqrt( this.b );
  
  			return this;
  
  		},
  
  		getHex: function () {
  
  			return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
  
  		},
  
  		getHexString: function () {
  
  			return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
  
  		},
  
  		getHSL: function ( optionalTarget ) {
  
  			// h,s,l ranges are in 0.0 - 1.0
  
  			var hsl = optionalTarget || { h: 0, s: 0, l: 0 };
  
  			var r = this.r, g = this.g, b = this.b;
  
  			var max = Math.max( r, g, b );
  			var min = Math.min( r, g, b );
  
  			var hue, saturation;
  			var lightness = ( min + max ) / 2.0;
  
  			if ( min === max ) {
  
  				hue = 0;
  				saturation = 0;
  
  			} else {
  
  				var delta = max - min;
  
  				saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
  
  				switch ( max ) {
  
  					case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
  					case g: hue = ( b - r ) / delta + 2; break;
  					case b: hue = ( r - g ) / delta + 4; break;
  
  				}
  
  				hue /= 6;
  
  			}
  
  			hsl.h = hue;
  			hsl.s = saturation;
  			hsl.l = lightness;
  
  			return hsl;
  
  		},
  
  		getStyle: function () {
  
  			return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
  
  		},
  
  		offsetHSL: function ( h, s, l ) {
  
  			var hsl = this.getHSL();
  
  			hsl.h += h; hsl.s += s; hsl.l += l;
  
  			this.setHSL( hsl.h, hsl.s, hsl.l );
  
  			return this;
  
  		},
  
  		add: function ( color ) {
  
  			this.r += color.r;
  			this.g += color.g;
  			this.b += color.b;
  
  			return this;
  
  		},
  
  		addColors: function ( color1, color2 ) {
  
  			this.r = color1.r + color2.r;
  			this.g = color1.g + color2.g;
  			this.b = color1.b + color2.b;
  
  			return this;
  
  		},
  
  		addScalar: function ( s ) {
  
  			this.r += s;
  			this.g += s;
  			this.b += s;
  
  			return this;
  
  		},
  
  		sub: function ( color ) {
  
  			this.r = Math.max( 0, this.r - color.r );
  			this.g = Math.max( 0, this.g - color.g );
  			this.b = Math.max( 0, this.b - color.b );
  
  			return this;
  
  		},
  
  		multiply: function ( color ) {
  
  			this.r *= color.r;
  			this.g *= color.g;
  			this.b *= color.b;
  
  			return this;
  
  		},
  
  		multiplyScalar: function ( s ) {
  
  			this.r *= s;
  			this.g *= s;
  			this.b *= s;
  
  			return this;
  
  		},
  
  		lerp: function ( color, alpha ) {
  
  			this.r += ( color.r - this.r ) * alpha;
  			this.g += ( color.g - this.g ) * alpha;
  			this.b += ( color.b - this.b ) * alpha;
  
  			return this;
  
  		},
  
  		equals: function ( c ) {
  
  			return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this.r = array[ offset ];
  			this.g = array[ offset + 1 ];
  			this.b = array[ offset + 2 ];
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this.r;
  			array[ offset + 1 ] = this.g;
  			array[ offset + 2 ] = this.b;
  
  			return array;
  
  		},
  
  		toJSON: function () {
  
  			return this.getHex();
  
  		}
  
  	} );
  
  	function EventDispatcher() {}
  
  	Object.assign( EventDispatcher.prototype, {
  
  		addEventListener: function ( type, listener ) {
  
  			if ( this._listeners === undefined ) this._listeners = {};
  
  			var listeners = this._listeners;
  
  			if ( listeners[ type ] === undefined ) {
  
  				listeners[ type ] = [];
  
  			}
  
  			if ( listeners[ type ].indexOf( listener ) === - 1 ) {
  
  				listeners[ type ].push( listener );
  
  			}
  
  		},
  
  		hasEventListener: function ( type, listener ) {
  
  			if ( this._listeners === undefined ) return false;
  
  			var listeners = this._listeners;
  
  			return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
  
  		},
  
  		removeEventListener: function ( type, listener ) {
  
  			if ( this._listeners === undefined ) return;
  
  			var listeners = this._listeners;
  			var listenerArray = listeners[ type ];
  
  			if ( listenerArray !== undefined ) {
  
  				var index = listenerArray.indexOf( listener );
  
  				if ( index !== - 1 ) {
  
  					listenerArray.splice( index, 1 );
  
  				}
  
  			}
  
  		},
  
  		dispatchEvent: function ( event ) {
  
  			if ( this._listeners === undefined ) return;
  
  			var listeners = this._listeners;
  			var listenerArray = listeners[ event.type ];
  
  			if ( listenerArray !== undefined ) {
  
  				event.target = this;
  
  				var array = listenerArray.slice( 0 );
  
  				for ( var i = 0, l = array.length; i < l; i ++ ) {
  
  					array[ i ].call( this, event );
  
  				}
  
  			}
  
  		}
  
  	} );
  
  	var FrontSide = 0;
  	var BackSide = 1;
  	var FlatShading = 1;
  	var NoColors = 0;
  	var NormalBlending = 1;
  	var AddEquation = 100;
  	var SrcAlphaFactor = 204;
  	var OneMinusSrcAlphaFactor = 205;
  	var LessEqualDepth = 3;
  
  	var materialId = 0;
  
  	function Material() {
  
  		Object.defineProperty( this, 'id', { value: materialId ++ } );
  
  		this.uuid = _Math.generateUUID();
  
  		this.name = '';
  		this.type = 'Material';
  
  		this.fog = true;
  		this.lights = true;
  
  		this.blending = NormalBlending;
  		this.side = FrontSide;
  		this.flatShading = false;
  		this.vertexColors = NoColors; // NoColors, VertexColors, FaceColors
  
  		this.opacity = 1;
  		this.transparent = false;
  
  		this.blendSrc = SrcAlphaFactor;
  		this.blendDst = OneMinusSrcAlphaFactor;
  		this.blendEquation = AddEquation;
  		this.blendSrcAlpha = null;
  		this.blendDstAlpha = null;
  		this.blendEquationAlpha = null;
  
  		this.depthFunc = LessEqualDepth;
  		this.depthTest = true;
  		this.depthWrite = true;
  
  		this.clippingPlanes = null;
  		this.clipIntersection = false;
  		this.clipShadows = false;
  
  		this.shadowSide = null;
  
  		this.colorWrite = true;
  
  		this.precision = null; // override the renderer's default precision for this material
  
  		this.polygonOffset = false;
  		this.polygonOffsetFactor = 0;
  		this.polygonOffsetUnits = 0;
  
  		this.dithering = false;
  
  		this.alphaTest = 0;
  		this.premultipliedAlpha = false;
  
  		this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer
  
  		this.visible = true;
  
  		this.userData = {};
  
  		this.needsUpdate = true;
  
  	}
  
  	Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
  		constructor: Material,
  
  		isMaterial: true,
  
  		onBeforeCompile: function () {},
  
  		setValues: function ( values ) {
  
  			if ( values === undefined ) return;
  
  			for ( var key in values ) {
  
  				var newValue = values[ key ];
  
  				if ( newValue === undefined ) {
  
  					console.warn( "Material: '" + key + "' parameter is undefined." );
  					continue;
  
  				}
  
  				// for backward compatability if shading is set in the constructor
  				if ( key === 'shading' ) {
  
  					console.warn( '' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
  					this.flatShading = ( newValue === FlatShading ) ? true : false;
  					continue;
  
  				}
  
  				var currentValue = this[ key ];
  
  				if ( currentValue === undefined ) {
  
  					console.warn( "" + this.type + ": '" + key + "' is not a property of this material." );
  					continue;
  
  				}
  
  				if ( currentValue && currentValue.isColor ) {
  
  					currentValue.set( newValue );
  
  				} else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {
  
  					currentValue.copy( newValue );
  
  				} else if ( key === 'overdraw' ) {
  
  					// ensure overdraw is backwards-compatible with legacy boolean type
  					this[ key ] = Number( newValue );
  
  				} else {
  
  					this[ key ] = newValue;
  
  				}
  
  			}
  
  		},
  
  		toJSON: function ( meta ) {
  
  			var isRoot = ( meta === undefined || typeof meta === 'string' );
  
  			if ( isRoot ) {
  
  				meta = {
  					textures: {},
  					images: {}
  				};
  
  			}
  
  			var data = {
  				metadata: {
  					version: 4.5,
  					type: 'Material',
  					generator: 'Material.toJSON'
  				}
  			};
  
  			// standard Material serialization
  			data.uuid = this.uuid;
  			data.type = this.type;
  
  			if ( this.name !== '' ) data.name = this.name;
  
  			if ( this.color && this.color.isColor ) data.color = this.color.getHex();
  
  			if ( this.roughness !== undefined ) data.roughness = this.roughness;
  			if ( this.metalness !== undefined ) data.metalness = this.metalness;
  
  			if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();
  			if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;
  
  			if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
  			if ( this.shininess !== undefined ) data.shininess = this.shininess;
  			if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat;
  			if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness;
  
  			if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;
  			if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
  			if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
  			if ( this.bumpMap && this.bumpMap.isTexture ) {
  
  				data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
  				data.bumpScale = this.bumpScale;
  
  			}
  			if ( this.normalMap && this.normalMap.isTexture ) {
  
  				data.normalMap = this.normalMap.toJSON( meta ).uuid;
  				data.normalScale = this.normalScale.toArray();
  
  			}
  			if ( this.displacementMap && this.displacementMap.isTexture ) {
  
  				data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
  				data.displacementScale = this.displacementScale;
  				data.displacementBias = this.displacementBias;
  
  			}
  			if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;
  			if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;
  
  			if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;
  			if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
  
  			if ( this.envMap && this.envMap.isTexture ) {
  
  				data.envMap = this.envMap.toJSON( meta ).uuid;
  				data.reflectivity = this.reflectivity; // Scale behind envMap
  
  			}
  
  			if ( this.gradientMap && this.gradientMap.isTexture ) {
  
  				data.gradientMap = this.gradientMap.toJSON( meta ).uuid;
  
  			}
  
  			if ( this.size !== undefined ) data.size = this.size;
  			if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
  
  			if ( this.blending !== NormalBlending ) data.blending = this.blending;
  			if ( this.flatShading === true ) data.flatShading = this.flatShading;
  			if ( this.side !== FrontSide ) data.side = this.side;
  			if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors;
  
  			if ( this.opacity < 1 ) data.opacity = this.opacity;
  			if ( this.transparent === true ) data.transparent = this.transparent;
  
  			data.depthFunc = this.depthFunc;
  			data.depthTest = this.depthTest;
  			data.depthWrite = this.depthWrite;
  
  			// rotation (SpriteMaterial)
  			if ( this.rotation !== 0 ) data.rotation = this.rotation;
  
  			if ( this.linewidth !== 1 ) data.linewidth = this.linewidth;
  			if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;
  			if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;
  			if ( this.scale !== undefined ) data.scale = this.scale;
  
  			if ( this.dithering === true ) data.dithering = true;
  
  			if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
  			if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
  
  			if ( this.wireframe === true ) data.wireframe = this.wireframe;
  			if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
  			if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
  			if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
  
  			if ( this.morphTargets === true ) data.morphTargets = true;
  			if ( this.skinning === true ) data.skinning = true;
  
  			if ( this.visible === false ) data.visible = false;
  			if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
  
  			// TODO: Copied from Object3D.toJSON
  
  			function extractFromCache( cache ) {
  
  				var values = [];
  
  				for ( var key in cache ) {
  
  					var data = cache[ key ];
  					delete data.metadata;
  					values.push( data );
  
  				}
  
  				return values;
  
  			}
  
  			if ( isRoot ) {
  
  				var textures = extractFromCache( meta.textures );
  				var images = extractFromCache( meta.images );
  
  				if ( textures.length > 0 ) data.textures = textures;
  				if ( images.length > 0 ) data.images = images;
  
  			}
  
  			return data;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor().copy( this );
  
  		},
  
  		copy: function ( source ) {
  
  			this.name = source.name;
  
  			this.fog = source.fog;
  			this.lights = source.lights;
  
  			this.blending = source.blending;
  			this.side = source.side;
  			this.flatShading = source.flatShading;
  			this.vertexColors = source.vertexColors;
  
  			this.opacity = source.opacity;
  			this.transparent = source.transparent;
  
  			this.blendSrc = source.blendSrc;
  			this.blendDst = source.blendDst;
  			this.blendEquation = source.blendEquation;
  			this.blendSrcAlpha = source.blendSrcAlpha;
  			this.blendDstAlpha = source.blendDstAlpha;
  			this.blendEquationAlpha = source.blendEquationAlpha;
  
  			this.depthFunc = source.depthFunc;
  			this.depthTest = source.depthTest;
  			this.depthWrite = source.depthWrite;
  
  			this.colorWrite = source.colorWrite;
  
  			this.precision = source.precision;
  
  			this.polygonOffset = source.polygonOffset;
  			this.polygonOffsetFactor = source.polygonOffsetFactor;
  			this.polygonOffsetUnits = source.polygonOffsetUnits;
  
  			this.dithering = source.dithering;
  
  			this.alphaTest = source.alphaTest;
  			this.premultipliedAlpha = source.premultipliedAlpha;
  
  			this.overdraw = source.overdraw;
  
  			this.visible = source.visible;
  			this.userData = JSON.parse( JSON.stringify( source.userData ) );
  
  			this.clipShadows = source.clipShadows;
  			this.clipIntersection = source.clipIntersection;
  
  			var srcPlanes = source.clippingPlanes,
  				dstPlanes = null;
  
  			if ( srcPlanes !== null ) {
  
  				var n = srcPlanes.length;
  				dstPlanes = new Array( n );
  
  				for ( var i = 0; i !== n; ++ i )
  					dstPlanes[ i ] = srcPlanes[ i ].clone();
  
  			}
  
  			this.clippingPlanes = dstPlanes;
  
  			this.shadowSide = source.shadowSide;
  
  			return this;
  
  		},
  
  		dispose: function () {
  
  			this.dispatchEvent( { type: 'dispose' } );
  
  		}
  
  	} );
  
  	var UniformsUtils = {
  
  		merge: function ( uniforms ) {
  
  			var merged = {};
  
  			for ( var u = 0; u < uniforms.length; u ++ ) {
  
  				var tmp = this.clone( uniforms[ u ] );
  
  				for ( var p in tmp ) {
  
  					merged[ p ] = tmp[ p ];
  
  				}
  
  			}
  
  			return merged;
  
  		},
  
  		clone: function ( uniforms_src ) {
  
  			var uniforms_dst = {};
  
  			for ( var u in uniforms_src ) {
  
  				uniforms_dst[ u ] = {};
  
  				for ( var p in uniforms_src[ u ] ) {
  
  					var parameter_src = uniforms_src[ u ][ p ];
  
  					if ( parameter_src && ( parameter_src.isColor ||
  						parameter_src.isMatrix3 || parameter_src.isMatrix4 ||
  						parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 ||
  						parameter_src.isTexture ) ) {
  
  						uniforms_dst[ u ][ p ] = parameter_src.clone();
  
  					} else if ( Array.isArray( parameter_src ) ) {
  
  						uniforms_dst[ u ][ p ] = parameter_src.slice();
  
  					} else {
  
  						uniforms_dst[ u ][ p ] = parameter_src;
  
  					}
  
  				}
  
  			}
  
  			return uniforms_dst;
  
  		}
  
  	};
  
  	function ShaderMaterial( parameters ) {
  
  		Material.call( this );
  
  		this.type = 'ShaderMaterial';
  
  		this.defines = {};
  		this.uniforms = {};
  
  		this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}';
  		this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}';
  
  		this.linewidth = 1;
  
  		this.wireframe = false;
  		this.wireframeLinewidth = 1;
  
  		this.fog = false; // set to use scene fog
  		this.lights = false; // set to use scene lights
  		this.clipping = false; // set to use user-defined clipping planes
  
  		this.skinning = false; // set to use skinning attribute streams
  		this.morphTargets = false; // set to use morph targets
  		this.morphNormals = false; // set to use morph normals
  
  		this.extensions = {
  			derivatives: false, // set to use derivatives
  			fragDepth: false, // set to use fragment depth values
  			drawBuffers: false, // set to use draw buffers
  			shaderTextureLOD: false // set to use shader texture LOD
  		};
  
  		// When rendered geometry doesn't include these attributes but the material does,
  		// use these default values in WebGL. This avoids errors when buffer data is missing.
  		this.defaultAttributeValues = {
  			'color': [ 1, 1, 1 ],
  			'uv': [ 0, 0 ],
  			'uv2': [ 0, 0 ]
  		};
  
  		this.index0AttributeName = undefined;
  		this.uniformsNeedUpdate = false;
  
  		if ( parameters !== undefined ) {
  
  			if ( parameters.attributes !== undefined ) {
  
  				console.error( 'ShaderMaterial: attributes should now be defined in BufferGeometry instead.' );
  
  			}
  
  			this.setValues( parameters );
  
  		}
  
  	}
  
  	ShaderMaterial.prototype = Object.create( Material.prototype );
  	ShaderMaterial.prototype.constructor = ShaderMaterial;
  
  	ShaderMaterial.prototype.isShaderMaterial = true;
  
  	ShaderMaterial.prototype.copy = function ( source ) {
  
  		Material.prototype.copy.call( this, source );
  
  		this.fragmentShader = source.fragmentShader;
  		this.vertexShader = source.vertexShader;
  
  		this.uniforms = UniformsUtils.clone( source.uniforms );
  
  		this.defines = source.defines;
  
  		this.wireframe = source.wireframe;
  		this.wireframeLinewidth = source.wireframeLinewidth;
  
  		this.lights = source.lights;
  		this.clipping = source.clipping;
  
  		this.skinning = source.skinning;
  
  		this.morphTargets = source.morphTargets;
  		this.morphNormals = source.morphNormals;
  
  		this.extensions = source.extensions;
  
  		return this;
  
  	};
  
  	ShaderMaterial.prototype.toJSON = function ( meta ) {
  
  		var data = Material.prototype.toJSON.call( this, meta );
  
  		data.uniforms = this.uniforms;
  		data.vertexShader = this.vertexShader;
  		data.fragmentShader = this.fragmentShader;
  
  		return data;
  
  	};
  
  	var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n";
  
  	var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n";
  
  	var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n";
  
  	var 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";
  
  	var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif";
  
  	var begin_vertex = "\nvec3 transformed = vec3( position );\n";
  
  	var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n";
  
  	var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t}\n\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}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE  = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS  = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\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";
  
  	var 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 = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\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";
  
  	var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif\n";
  
  	var 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";
  
  	var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n";
  
  	var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n";
  
  	var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif";
  
  	var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n";
  
  	var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif";
  
  	var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif";
  
  	var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\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}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\n";
  
  	var 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";
  
  	var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n";
  
  	var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n";
  
  	var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n";
  
  	var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n";
  
  	var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n";
  
  	var encodings_fragment = "  gl_FragColor = linearToOutputTexel( gl_FragColor );\n";
  
  	var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn 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\treturn 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\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat M      = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM            = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat D      = max( maxRange / maxRGB, 1.0 );\n\tD            = min( floor( D ) / 255.0, 1.0 );\n\treturn 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\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract(Le);\n\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n\treturn 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\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n\treturn vec4( max(vRGB, 0.0), 1.0 );\n}\n";
  
  	var 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, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = 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";
  
  	var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\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";
  
  	var 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";
  
  	var 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";
  
  	var fog_vertex = "\n#ifdef USE_FOG\nfogDepth = -mvPosition.z;\n#endif";
  
  	var fog_pars_vertex = "#ifdef USE_FOG\n  varying float fogDepth;\n#endif\n";
  
  	var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n";
  
  	var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\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";
  
  	var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n";
  
  	var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n";
  
  	var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif";
  
  	var 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\t#pragma unroll_loop\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\t#pragma unroll_loop\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\t#pragma unroll_loop\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\t#pragma unroll_loop\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";
  
  	var 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\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\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\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\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 ( angleCos > spotLight.coneCos ) {\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_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\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\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = 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 = 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\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = 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 = 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 = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, 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 = 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";
  
  	var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n";
  
  	var 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\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\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";
  
  	var 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";
  
  	var 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}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3(    0, 1,    0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\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_Direct_RectArea\t\tRE_Direct_RectArea_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";
  
  	var 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\t#pragma unroll_loop\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 ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 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\t#pragma unroll_loop\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\t#pragma unroll_loop\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 ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, 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\t#pragma unroll_loop\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\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n";
  
  	var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif";
  
  	var 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";
  
  	var 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";
  
  	var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif\n";
  
  	var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n";
  
  	var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n";
  
  	var map_particle_fragment = "#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n";
  
  	var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif\n";
  
  	var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n";
  
  	var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
  
  	var 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";
  
  	var 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";
  
  	var 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";
  
  	var 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 );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\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";
  
  	var 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 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\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";
  
  	var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\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\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n";
  
  	var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n";
  
  	var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n";
  
  	var dithering_fragment = "#if defined( DITHERING )\n  gl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif\n";
  
  	var dithering_pars_fragment = "#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif\n";
  
  	var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n";
  
  	var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
  
  	var 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\t\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\tfloat shadow = 1.0;\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\tshadow = (\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\tshadow = (\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\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\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, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\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";
  
  	var 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\t\n#endif\n";
  
  	var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\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\t#pragma unroll_loop\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\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t\n#endif\n";
  
  	var 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\t#pragma unroll_loop\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\t#pragma unroll_loop\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\t#pragma unroll_loop\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 ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t\n\t#endif\n\treturn shadow;\n}\n";
  
  	var 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";
  
  	var 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 boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\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";
  
  	var 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\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif\n";
  
  	var 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";
  
  	var 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";
  
  	var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif";
  
  	var tonemapping_fragment = "#if defined( TONE_MAPPING )\n  gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n";
  
  	var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn 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\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
  
  	var 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";
  
  	var 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 mat3 uvTransform;\n#endif\n";
  
  	var 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 = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif";
  
  	var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif";
  
  	var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif";
  
  	var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif";
  
  	var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n";
  
  	var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
  
  	var 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\tgl_Position.z = gl_Position.w;\n}\n";
  
  	var 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";
  
  	var 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#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n}\n";
  
  	var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main () {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}\n";
  
  	var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\tvWorldPosition = worldPosition.xyz;\n}\n";
  
  	var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
  
  	var 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";
  
  	var 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";
  
  	var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_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\t#include <fog_vertex>\n}\n";
  
  	var 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 <lightmap_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 = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include <aomap_fragment>\n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\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";
  
  	var 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 <fog_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\t#include <fog_vertex>\n}\n";
  
  	var 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 <dithering_pars_fragment>\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 <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n";
  
  	var 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 <fog_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\t#include <fog_vertex>\n}\n";
  
  	var 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 <dithering_pars_fragment>\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 <gradientmap_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_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 <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n";
  
  	var 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 <fog_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 <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_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\t#include <fog_vertex>\n}\n";
  
  	var 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\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\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 <roughnessmap_fragment>\n\t#include <metalnessmap_fragment>\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 <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n";
  
  	var 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 <fog_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 <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_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\t#include <fog_vertex>\n}\n";
  
  	var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <packing>\n#include <uv_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\nvoid main() {\n\t#include <logdepthbuf_fragment>\n\t#include <normal_fragment>\n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}\n";
  
  	var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\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>\nvoid main() {\n\t#include <uv_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 <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n";
  
  	var 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";
  
  	var points_vert = "uniform float size;\nuniform float scale;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_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\t#include <fog_vertex>\n}\n";
  
  	var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include <fog_fragment>\n}\n";
  
  	var shadow_vert = "#include <fog_pars_vertex>\n#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\t#include <fog_vertex>\n}\n";
  
  	var ShaderChunk = {
  		alphamap_fragment: alphamap_fragment,
  		alphamap_pars_fragment: alphamap_pars_fragment,
  		alphatest_fragment: alphatest_fragment,
  		aomap_fragment: aomap_fragment,
  		aomap_pars_fragment: aomap_pars_fragment,
  		begin_vertex: begin_vertex,
  		beginnormal_vertex: beginnormal_vertex,
  		bsdfs: bsdfs,
  		bumpmap_pars_fragment: bumpmap_pars_fragment,
  		clipping_planes_fragment: clipping_planes_fragment,
  		clipping_planes_pars_fragment: clipping_planes_pars_fragment,
  		clipping_planes_pars_vertex: clipping_planes_pars_vertex,
  		clipping_planes_vertex: clipping_planes_vertex,
  		color_fragment: color_fragment,
  		color_pars_fragment: color_pars_fragment,
  		color_pars_vertex: color_pars_vertex,
  		color_vertex: color_vertex,
  		common: common,
  		cube_uv_reflection_fragment: cube_uv_reflection_fragment,
  		defaultnormal_vertex: defaultnormal_vertex,
  		displacementmap_pars_vertex: displacementmap_pars_vertex,
  		displacementmap_vertex: displacementmap_vertex,
  		emissivemap_fragment: emissivemap_fragment,
  		emissivemap_pars_fragment: emissivemap_pars_fragment,
  		encodings_fragment: encodings_fragment,
  		encodings_pars_fragment: encodings_pars_fragment,
  		envmap_fragment: envmap_fragment,
  		envmap_pars_fragment: envmap_pars_fragment,
  		envmap_pars_vertex: envmap_pars_vertex,
  		envmap_vertex: envmap_vertex,
  		fog_vertex: fog_vertex,
  		fog_pars_vertex: fog_pars_vertex,
  		fog_fragment: fog_fragment,
  		fog_pars_fragment: fog_pars_fragment,
  		gradientmap_pars_fragment: gradientmap_pars_fragment,
  		lightmap_fragment: lightmap_fragment,
  		lightmap_pars_fragment: lightmap_pars_fragment,
  		lights_lambert_vertex: lights_lambert_vertex,
  		lights_pars: lights_pars,
  		lights_phong_fragment: lights_phong_fragment,
  		lights_phong_pars_fragment: lights_phong_pars_fragment,
  		lights_physical_fragment: lights_physical_fragment,
  		lights_physical_pars_fragment: lights_physical_pars_fragment,
  		lights_template: lights_template,
  		logdepthbuf_fragment: logdepthbuf_fragment,
  		logdepthbuf_pars_fragment: logdepthbuf_pars_fragment,
  		logdepthbuf_pars_vertex: logdepthbuf_pars_vertex,
  		logdepthbuf_vertex: logdepthbuf_vertex,
  		map_fragment: map_fragment,
  		map_pars_fragment: map_pars_fragment,
  		map_particle_fragment: map_particle_fragment,
  		map_particle_pars_fragment: map_particle_pars_fragment,
  		metalnessmap_fragment: metalnessmap_fragment,
  		metalnessmap_pars_fragment: metalnessmap_pars_fragment,
  		morphnormal_vertex: morphnormal_vertex,
  		morphtarget_pars_vertex: morphtarget_pars_vertex,
  		morphtarget_vertex: morphtarget_vertex,
  		normal_fragment: normal_fragment,
  		normalmap_pars_fragment: normalmap_pars_fragment,
  		packing: packing,
  		premultiplied_alpha_fragment: premultiplied_alpha_fragment,
  		project_vertex: project_vertex,
  		dithering_fragment: dithering_fragment,
  		dithering_pars_fragment: dithering_pars_fragment,
  		roughnessmap_fragment: roughnessmap_fragment,
  		roughnessmap_pars_fragment: roughnessmap_pars_fragment,
  		shadowmap_pars_fragment: shadowmap_pars_fragment,
  		shadowmap_pars_vertex: shadowmap_pars_vertex,
  		shadowmap_vertex: shadowmap_vertex,
  		shadowmask_pars_fragment: shadowmask_pars_fragment,
  		skinbase_vertex: skinbase_vertex,
  		skinning_pars_vertex: skinning_pars_vertex,
  		skinning_vertex: skinning_vertex,
  		skinnormal_vertex: skinnormal_vertex,
  		specularmap_fragment: specularmap_fragment,
  		specularmap_pars_fragment: specularmap_pars_fragment,
  		tonemapping_fragment: tonemapping_fragment,
  		tonemapping_pars_fragment: tonemapping_pars_fragment,
  		uv_pars_fragment: uv_pars_fragment,
  		uv_pars_vertex: uv_pars_vertex,
  		uv_vertex: uv_vertex,
  		uv2_pars_fragment: uv2_pars_fragment,
  		uv2_pars_vertex: uv2_pars_vertex,
  		uv2_vertex: uv2_vertex,
  		worldpos_vertex: worldpos_vertex,
  
  		cube_frag: cube_frag,
  		cube_vert: cube_vert,
  		depth_frag: depth_frag,
  		depth_vert: depth_vert,
  		distanceRGBA_frag: distanceRGBA_frag,
  		distanceRGBA_vert: distanceRGBA_vert,
  		equirect_frag: equirect_frag,
  		equirect_vert: equirect_vert,
  		linedashed_frag: linedashed_frag,
  		linedashed_vert: linedashed_vert,
  		meshbasic_frag: meshbasic_frag,
  		meshbasic_vert: meshbasic_vert,
  		meshlambert_frag: meshlambert_frag,
  		meshlambert_vert: meshlambert_vert,
  		meshphong_frag: meshphong_frag,
  		meshphong_vert: meshphong_vert,
  		meshphysical_frag: meshphysical_frag,
  		meshphysical_vert: meshphysical_vert,
  		normal_frag: normal_frag,
  		normal_vert: normal_vert,
  		points_frag: points_frag,
  		points_vert: points_vert,
  		shadow_frag: shadow_frag,
  		shadow_vert: shadow_vert
  	};
  
  	function Matrix4() {
  
  		this.elements = [
  
  			1, 0, 0, 0,
  			0, 1, 0, 0,
  			0, 0, 1, 0,
  			0, 0, 0, 1
  
  		];
  
  		if ( arguments.length > 0 ) {
  
  			console.error( 'Matrix4: the constructor no longer reads arguments. use .set() instead.' );
  
  		}
  
  	}
  
  	Object.assign( Matrix4.prototype, {
  
  		isMatrix4: true,
  
  		set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
  
  			var te = this.elements;
  
  			te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
  			te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
  			te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
  			te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
  
  			return this;
  
  		},
  
  		identity: function () {
  
  			this.set(
  
  				1, 0, 0, 0,
  				0, 1, 0, 0,
  				0, 0, 1, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new Matrix4().fromArray( this.elements );
  
  		},
  
  		copy: function ( m ) {
  
  			var te = this.elements;
  			var me = m.elements;
  
  			te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
  			te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
  			te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
  			te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
  
  			return this;
  
  		},
  
  		copyPosition: function ( m ) {
  
  			var te = this.elements, me = m.elements;
  
  			te[ 12 ] = me[ 12 ];
  			te[ 13 ] = me[ 13 ];
  			te[ 14 ] = me[ 14 ];
  
  			return this;
  
  		},
  
  		extractBasis: function ( xAxis, yAxis, zAxis ) {
  
  			xAxis.setFromMatrixColumn( this, 0 );
  			yAxis.setFromMatrixColumn( this, 1 );
  			zAxis.setFromMatrixColumn( this, 2 );
  
  			return this;
  
  		},
  
  		makeBasis: function ( xAxis, yAxis, zAxis ) {
  
  			this.set(
  				xAxis.x, yAxis.x, zAxis.x, 0,
  				xAxis.y, yAxis.y, zAxis.y, 0,
  				xAxis.z, yAxis.z, zAxis.z, 0,
  				0, 0, 0, 1
  			);
  
  			return this;
  
  		},
  
  		extractRotation: function () {
  
  			var v1 = new Vector3();
  
  			return function extractRotation( m ) {
  
  				var te = this.elements;
  				var me = m.elements;
  
  				var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
  				var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
  				var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
  
  				te[ 0 ] = me[ 0 ] * scaleX;
  				te[ 1 ] = me[ 1 ] * scaleX;
  				te[ 2 ] = me[ 2 ] * scaleX;
  
  				te[ 4 ] = me[ 4 ] * scaleY;
  				te[ 5 ] = me[ 5 ] * scaleY;
  				te[ 6 ] = me[ 6 ] * scaleY;
  
  				te[ 8 ] = me[ 8 ] * scaleZ;
  				te[ 9 ] = me[ 9 ] * scaleZ;
  				te[ 10 ] = me[ 10 ] * scaleZ;
  
  				return this;
  
  			};
  
  		}(),
  
  		makeRotationFromEuler: function ( euler ) {
  
  			if ( ! ( euler && euler.isEuler ) ) {
  
  				console.error( 'Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
  
  			}
  
  			var te = this.elements;
  
  			var x = euler.x, y = euler.y, z = euler.z;
  			var a = Math.cos( x ), b = Math.sin( x );
  			var c = Math.cos( y ), d = Math.sin( y );
  			var e = Math.cos( z ), f = Math.sin( z );
  
  			if ( euler.order === 'XYZ' ) {
  
  				var ae = a * e, af = a * f, be = b * e, bf = b * f;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = - c * f;
  				te[ 8 ] = d;
  
  				te[ 1 ] = af + be * d;
  				te[ 5 ] = ae - bf * d;
  				te[ 9 ] = - b * c;
  
  				te[ 2 ] = bf - ae * d;
  				te[ 6 ] = be + af * d;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'YXZ' ) {
  
  				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
  				te[ 0 ] = ce + df * b;
  				te[ 4 ] = de * b - cf;
  				te[ 8 ] = a * d;
  
  				te[ 1 ] = a * f;
  				te[ 5 ] = a * e;
  				te[ 9 ] = - b;
  
  				te[ 2 ] = cf * b - de;
  				te[ 6 ] = df + ce * b;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'ZXY' ) {
  
  				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
  				te[ 0 ] = ce - df * b;
  				te[ 4 ] = - a * f;
  				te[ 8 ] = de + cf * b;
  
  				te[ 1 ] = cf + de * b;
  				te[ 5 ] = a * e;
  				te[ 9 ] = df - ce * b;
  
  				te[ 2 ] = - a * d;
  				te[ 6 ] = b;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'ZYX' ) {
  
  				var ae = a * e, af = a * f, be = b * e, bf = b * f;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = be * d - af;
  				te[ 8 ] = ae * d + bf;
  
  				te[ 1 ] = c * f;
  				te[ 5 ] = bf * d + ae;
  				te[ 9 ] = af * d - be;
  
  				te[ 2 ] = - d;
  				te[ 6 ] = b * c;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'YZX' ) {
  
  				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = bd - ac * f;
  				te[ 8 ] = bc * f + ad;
  
  				te[ 1 ] = f;
  				te[ 5 ] = a * e;
  				te[ 9 ] = - b * e;
  
  				te[ 2 ] = - d * e;
  				te[ 6 ] = ad * f + bc;
  				te[ 10 ] = ac - bd * f;
  
  			} else if ( euler.order === 'XZY' ) {
  
  				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = - f;
  				te[ 8 ] = d * e;
  
  				te[ 1 ] = ac * f + bd;
  				te[ 5 ] = a * e;
  				te[ 9 ] = ad * f - bc;
  
  				te[ 2 ] = bc * f - ad;
  				te[ 6 ] = b * e;
  				te[ 10 ] = bd * f + ac;
  
  			}
  
  			// last column
  			te[ 3 ] = 0;
  			te[ 7 ] = 0;
  			te[ 11 ] = 0;
  
  			// bottom row
  			te[ 12 ] = 0;
  			te[ 13 ] = 0;
  			te[ 14 ] = 0;
  			te[ 15 ] = 1;
  
  			return this;
  
  		},
  
  		makeRotationFromQuaternion: function ( q ) {
  
  			var te = this.elements;
  
  			var x = q._x, y = q._y, z = q._z, w = q._w;
  			var x2 = x + x, y2 = y + y, z2 = z + z;
  			var xx = x * x2, xy = x * y2, xz = x * z2;
  			var yy = y * y2, yz = y * z2, zz = z * z2;
  			var wx = w * x2, wy = w * y2, wz = w * z2;
  
  			te[ 0 ] = 1 - ( yy + zz );
  			te[ 4 ] = xy - wz;
  			te[ 8 ] = xz + wy;
  
  			te[ 1 ] = xy + wz;
  			te[ 5 ] = 1 - ( xx + zz );
  			te[ 9 ] = yz - wx;
  
  			te[ 2 ] = xz - wy;
  			te[ 6 ] = yz + wx;
  			te[ 10 ] = 1 - ( xx + yy );
  
  			// last column
  			te[ 3 ] = 0;
  			te[ 7 ] = 0;
  			te[ 11 ] = 0;
  
  			// bottom row
  			te[ 12 ] = 0;
  			te[ 13 ] = 0;
  			te[ 14 ] = 0;
  			te[ 15 ] = 1;
  
  			return this;
  
  		},
  
  		lookAt: function () {
  
  			var x = new Vector3();
  			var y = new Vector3();
  			var z = new Vector3();
  
  			return function lookAt( eye, target, up ) {
  
  				var te = this.elements;
  
  				z.subVectors( eye, target );
  
  				if ( z.lengthSq() === 0 ) {
  
  					// eye and target are in the same position
  
  					z.z = 1;
  
  				}
  
  				z.normalize();
  				x.crossVectors( up, z );
  
  				if ( x.lengthSq() === 0 ) {
  
  					// up and z are parallel
  
  					if ( Math.abs( up.z ) === 1 ) {
  
  						z.x += 0.0001;
  
  					} else {
  
  						z.z += 0.0001;
  
  					}
  
  					z.normalize();
  					x.crossVectors( up, z );
  
  				}
  
  				x.normalize();
  				y.crossVectors( z, x );
  
  				te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
  				te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
  				te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
  
  				return this;
  
  			};
  
  		}(),
  
  		multiply: function ( m, n ) {
  
  			if ( n !== undefined ) {
  
  				console.warn( 'Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
  				return this.multiplyMatrices( m, n );
  
  			}
  
  			return this.multiplyMatrices( this, m );
  
  		},
  
  		premultiply: function ( m ) {
  
  			return this.multiplyMatrices( m, this );
  
  		},
  
  		multiplyMatrices: function ( a, b ) {
  
  			var ae = a.elements;
  			var be = b.elements;
  			var te = this.elements;
  
  			var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
  			var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
  			var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
  			var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
  
  			var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
  			var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
  			var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
  			var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
  
  			te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
  			te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
  			te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
  			te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
  
  			te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
  			te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
  			te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
  			te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
  
  			te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
  			te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
  			te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
  			te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
  
  			te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
  			te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
  			te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
  			te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
  
  			return this;
  
  		},
  
  		multiplyScalar: function ( s ) {
  
  			var te = this.elements;
  
  			te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
  			te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
  			te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
  			te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
  
  			return this;
  
  		},
  
  		applyToBufferAttribute: function () {
  
  			var v1 = new Vector3();
  
  			return function applyToBufferAttribute( attribute ) {
  
  				for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
  					v1.x = attribute.getX( i );
  					v1.y = attribute.getY( i );
  					v1.z = attribute.getZ( i );
  
  					v1.applyMatrix4( this );
  
  					attribute.setXYZ( i, v1.x, v1.y, v1.z );
  
  				}
  
  				return attribute;
  
  			};
  
  		}(),
  
  		determinant: function () {
  
  			var te = this.elements;
  
  			var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
  			var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
  			var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
  			var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
  
  			//TODO: make this more efficient
  			//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
  
  			return (
  				n41 * (
  					+ n14 * n23 * n32
  					 - n13 * n24 * n32
  					 - n14 * n22 * n33
  					 + n12 * n24 * n33
  					 + n13 * n22 * n34
  					 - n12 * n23 * n34
  				) +
  				n42 * (
  					+ n11 * n23 * n34
  					 - n11 * n24 * n33
  					 + n14 * n21 * n33
  					 - n13 * n21 * n34
  					 + n13 * n24 * n31
  					 - n14 * n23 * n31
  				) +
  				n43 * (
  					+ n11 * n24 * n32
  					 - n11 * n22 * n34
  					 - n14 * n21 * n32
  					 + n12 * n21 * n34
  					 + n14 * n22 * n31
  					 - n12 * n24 * n31
  				) +
  				n44 * (
  					- n13 * n22 * n31
  					 - n11 * n23 * n32
  					 + n11 * n22 * n33
  					 + n13 * n21 * n32
  					 - n12 * n21 * n33
  					 + n12 * n23 * n31
  				)
  
  			);
  
  		},
  
  		transpose: function () {
  
  			var te = this.elements;
  			var tmp;
  
  			tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
  			tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
  			tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
  
  			tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
  			tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
  			tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
  
  			return this;
  
  		},
  
  		setPosition: function ( v ) {
  
  			var te = this.elements;
  
  			te[ 12 ] = v.x;
  			te[ 13 ] = v.y;
  			te[ 14 ] = v.z;
  
  			return this;
  
  		},
  
  		getInverse: function ( m, throwOnDegenerate ) {
  
  			// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
  			var te = this.elements,
  				me = m.elements,
  
  				n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
  				n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
  				n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
  				n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
  
  				t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
  				t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
  				t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
  				t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
  
  			var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
  
  			if ( det === 0 ) {
  
  				var msg = "Matrix4: .getInverse() can't invert matrix, determinant is 0";
  
  				if ( throwOnDegenerate === true ) {
  
  					throw new Error( msg );
  
  				} else {
  
  					console.warn( msg );
  
  				}
  
  				return this.identity();
  
  			}
  
  			var detInv = 1 / det;
  
  			te[ 0 ] = t11 * detInv;
  			te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
  			te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
  			te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
  
  			te[ 4 ] = t12 * detInv;
  			te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
  			te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
  			te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
  
  			te[ 8 ] = t13 * detInv;
  			te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
  			te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
  			te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
  
  			te[ 12 ] = t14 * detInv;
  			te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
  			te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
  			te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
  
  			return this;
  
  		},
  
  		scale: function ( v ) {
  
  			var te = this.elements;
  			var x = v.x, y = v.y, z = v.z;
  
  			te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
  			te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
  			te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
  			te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
  
  			return this;
  
  		},
  
  		getMaxScaleOnAxis: function () {
  
  			var te = this.elements;
  
  			var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
  			var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
  			var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
  
  			return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
  
  		},
  
  		makeTranslation: function ( x, y, z ) {
  
  			this.set(
  
  				1, 0, 0, x,
  				0, 1, 0, y,
  				0, 0, 1, z,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationX: function ( theta ) {
  
  			var c = Math.cos( theta ), s = Math.sin( theta );
  
  			this.set(
  
  				1, 0, 0, 0,
  				0, c, - s, 0,
  				0, s, c, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationY: function ( theta ) {
  
  			var c = Math.cos( theta ), s = Math.sin( theta );
  
  			this.set(
  
  				 c, 0, s, 0,
  				 0, 1, 0, 0,
  				- s, 0, c, 0,
  				 0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationZ: function ( theta ) {
  
  			var c = Math.cos( theta ), s = Math.sin( theta );
  
  			this.set(
  
  				c, - s, 0, 0,
  				s, c, 0, 0,
  				0, 0, 1, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationAxis: function ( axis, angle ) {
  
  			// Based on http://www.gamedev.net/reference/articles/article1199.asp
  
  			var c = Math.cos( angle );
  			var s = Math.sin( angle );
  			var t = 1 - c;
  			var x = axis.x, y = axis.y, z = axis.z;
  			var tx = t * x, ty = t * y;
  
  			this.set(
  
  				tx * x + c, tx * y - s * z, tx * z + s * y, 0,
  				tx * y + s * z, ty * y + c, ty * z - s * x, 0,
  				tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
  				0, 0, 0, 1
  
  			);
  
  			 return this;
  
  		},
  
  		makeScale: function ( x, y, z ) {
  
  			this.set(
  
  				x, 0, 0, 0,
  				0, y, 0, 0,
  				0, 0, z, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeShear: function ( x, y, z ) {
  
  			this.set(
  
  				1, y, z, 0,
  				x, 1, z, 0,
  				x, y, 1, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		compose: function ( position, quaternion, scale ) {
  
  			this.makeRotationFromQuaternion( quaternion );
  			this.scale( scale );
  			this.setPosition( position );
  
  			return this;
  
  		},
  
  		decompose: function () {
  
  			var vector = new Vector3();
  			var matrix = new Matrix4();
  
  			return function decompose( position, quaternion, scale ) {
  
  				var te = this.elements;
  
  				var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
  				var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
  				var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
  
  				// if determine is negative, we need to invert one scale
  				var det = this.determinant();
  				if ( det < 0 ) sx = - sx;
  
  				position.x = te[ 12 ];
  				position.y = te[ 13 ];
  				position.z = te[ 14 ];
  
  				// scale the rotation part
  				matrix.copy( this );
  
  				var invSX = 1 / sx;
  				var invSY = 1 / sy;
  				var invSZ = 1 / sz;
  
  				matrix.elements[ 0 ] *= invSX;
  				matrix.elements[ 1 ] *= invSX;
  				matrix.elements[ 2 ] *= invSX;
  
  				matrix.elements[ 4 ] *= invSY;
  				matrix.elements[ 5 ] *= invSY;
  				matrix.elements[ 6 ] *= invSY;
  
  				matrix.elements[ 8 ] *= invSZ;
  				matrix.elements[ 9 ] *= invSZ;
  				matrix.elements[ 10 ] *= invSZ;
  
  				quaternion.setFromRotationMatrix( matrix );
  
  				scale.x = sx;
  				scale.y = sy;
  				scale.z = sz;
  
  				return this;
  
  			};
  
  		}(),
  
  		makePerspective: function ( left, right, top, bottom, near, far ) {
  
  			if ( far === undefined ) {
  
  				console.warn( 'Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
  
  			}
  
  			var te = this.elements;
  			var x = 2 * near / ( right - left );
  			var y = 2 * near / ( top - bottom );
  
  			var a = ( right + left ) / ( right - left );
  			var b = ( top + bottom ) / ( top - bottom );
  			var c = - ( far + near ) / ( far - near );
  			var d = - 2 * far * near / ( far - near );
  
  			te[ 0 ] = x;	te[ 4 ] = 0;	te[ 8 ] = a;	te[ 12 ] = 0;
  			te[ 1 ] = 0;	te[ 5 ] = y;	te[ 9 ] = b;	te[ 13 ] = 0;
  			te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = c;	te[ 14 ] = d;
  			te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = - 1;	te[ 15 ] = 0;
  
  			return this;
  
  		},
  
  		makeOrthographic: function ( left, right, top, bottom, near, far ) {
  
  			var te = this.elements;
  			var w = 1.0 / ( right - left );
  			var h = 1.0 / ( top - bottom );
  			var p = 1.0 / ( far - near );
  
  			var x = ( right + left ) * w;
  			var y = ( top + bottom ) * h;
  			var z = ( far + near ) * p;
  
  			te[ 0 ] = 2 * w;	te[ 4 ] = 0;	te[ 8 ] = 0;	te[ 12 ] = - x;
  			te[ 1 ] = 0;	te[ 5 ] = 2 * h;	te[ 9 ] = 0;	te[ 13 ] = - y;
  			te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = - 2 * p;	te[ 14 ] = - z;
  			te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = 0;	te[ 15 ] = 1;
  
  			return this;
  
  		},
  
  		equals: function ( matrix ) {
  
  			var te = this.elements;
  			var me = matrix.elements;
  
  			for ( var i = 0; i < 16; i ++ ) {
  
  				if ( te[ i ] !== me[ i ] ) return false;
  
  			}
  
  			return true;
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			for ( var i = 0; i < 16; i ++ ) {
  
  				this.elements[ i ] = array[ i + offset ];
  
  			}
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			var te = this.elements;
  
  			array[ offset ] = te[ 0 ];
  			array[ offset + 1 ] = te[ 1 ];
  			array[ offset + 2 ] = te[ 2 ];
  			array[ offset + 3 ] = te[ 3 ];
  
  			array[ offset + 4 ] = te[ 4 ];
  			array[ offset + 5 ] = te[ 5 ];
  			array[ offset + 6 ] = te[ 6 ];
  			array[ offset + 7 ] = te[ 7 ];
  
  			array[ offset + 8 ] = te[ 8 ];
  			array[ offset + 9 ] = te[ 9 ];
  			array[ offset + 10 ] = te[ 10 ];
  			array[ offset + 11 ] = te[ 11 ];
  
  			array[ offset + 12 ] = te[ 12 ];
  			array[ offset + 13 ] = te[ 13 ];
  			array[ offset + 14 ] = te[ 14 ];
  			array[ offset + 15 ] = te[ 15 ];
  
  			return array;
  
  		}
  
  	} );
  
  	function Quaternion( x, y, z, w ) {
  
  		this._x = x || 0;
  		this._y = y || 0;
  		this._z = z || 0;
  		this._w = ( w !== undefined ) ? w : 1;
  
  	}
  
  	Object.assign( Quaternion, {
  
  		slerp: function ( qa, qb, qm, t ) {
  
  			return qm.copy( qa ).slerp( qb, t );
  
  		},
  
  		slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
  
  			// fuzz-free, array-based Quaternion SLERP operation
  
  			var x0 = src0[ srcOffset0 + 0 ],
  				y0 = src0[ srcOffset0 + 1 ],
  				z0 = src0[ srcOffset0 + 2 ],
  				w0 = src0[ srcOffset0 + 3 ],
  
  				x1 = src1[ srcOffset1 + 0 ],
  				y1 = src1[ srcOffset1 + 1 ],
  				z1 = src1[ srcOffset1 + 2 ],
  				w1 = src1[ srcOffset1 + 3 ];
  
  			if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
  
  				var s = 1 - t,
  
  					cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
  
  					dir = ( cos >= 0 ? 1 : - 1 ),
  					sqrSin = 1 - cos * cos;
  
  				// Skip the Slerp for tiny steps to avoid numeric problems:
  				if ( sqrSin > Number.EPSILON ) {
  
  					var sin = Math.sqrt( sqrSin ),
  						len = Math.atan2( sin, cos * dir );
  
  					s = Math.sin( s * len ) / sin;
  					t = Math.sin( t * len ) / sin;
  
  				}
  
  				var tDir = t * dir;
  
  				x0 = x0 * s + x1 * tDir;
  				y0 = y0 * s + y1 * tDir;
  				z0 = z0 * s + z1 * tDir;
  				w0 = w0 * s + w1 * tDir;
  
  				// Normalize in case we just did a lerp:
  				if ( s === 1 - t ) {
  
  					var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
  
  					x0 *= f;
  					y0 *= f;
  					z0 *= f;
  					w0 *= f;
  
  				}
  
  			}
  
  			dst[ dstOffset ] = x0;
  			dst[ dstOffset + 1 ] = y0;
  			dst[ dstOffset + 2 ] = z0;
  			dst[ dstOffset + 3 ] = w0;
  
  		}
  
  	} );
  
  	Object.defineProperties( Quaternion.prototype, {
  
  		x: {
  
  			get: function () {
  
  				return this._x;
  
  			},
  
  			set: function ( value ) {
  
  				this._x = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		y: {
  
  			get: function () {
  
  				return this._y;
  
  			},
  
  			set: function ( value ) {
  
  				this._y = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		z: {
  
  			get: function () {
  
  				return this._z;
  
  			},
  
  			set: function ( value ) {
  
  				this._z = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		w: {
  
  			get: function () {
  
  				return this._w;
  
  			},
  
  			set: function ( value ) {
  
  				this._w = value;
  				this.onChangeCallback();
  
  			}
  
  		}
  
  	} );
  
  	Object.assign( Quaternion.prototype, {
  
  		set: function ( x, y, z, w ) {
  
  			this._x = x;
  			this._y = y;
  			this._z = z;
  			this._w = w;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this._x, this._y, this._z, this._w );
  
  		},
  
  		copy: function ( quaternion ) {
  
  			this._x = quaternion.x;
  			this._y = quaternion.y;
  			this._z = quaternion.z;
  			this._w = quaternion.w;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromEuler: function ( euler, update ) {
  
  			if ( ! ( euler && euler.isEuler ) ) {
  
  				throw new Error( 'Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
  			}
  
  			var x = euler._x, y = euler._y, z = euler._z, order = euler.order;
  
  			// http://www.mathworks.com/matlabcentral/fileexchange/
  			// 	20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
  			//	content/SpinCalc.m
  
  			var cos = Math.cos;
  			var sin = Math.sin;
  
  			var c1 = cos( x / 2 );
  			var c2 = cos( y / 2 );
  			var c3 = cos( z / 2 );
  
  			var s1 = sin( x / 2 );
  			var s2 = sin( y / 2 );
  			var s3 = sin( z / 2 );
  
  			if ( order === 'XYZ' ) {
  
  				this._x = s1 * c2 * c3 + c1 * s2 * s3;
  				this._y = c1 * s2 * c3 - s1 * c2 * s3;
  				this._z = c1 * c2 * s3 + s1 * s2 * c3;
  				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
  			} else if ( order === 'YXZ' ) {
  
  				this._x = s1 * c2 * c3 + c1 * s2 * s3;
  				this._y = c1 * s2 * c3 - s1 * c2 * s3;
  				this._z = c1 * c2 * s3 - s1 * s2 * c3;
  				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
  			} else if ( order === 'ZXY' ) {
  
  				this._x = s1 * c2 * c3 - c1 * s2 * s3;
  				this._y = c1 * s2 * c3 + s1 * c2 * s3;
  				this._z = c1 * c2 * s3 + s1 * s2 * c3;
  				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
  			} else if ( order === 'ZYX' ) {
  
  				this._x = s1 * c2 * c3 - c1 * s2 * s3;
  				this._y = c1 * s2 * c3 + s1 * c2 * s3;
  				this._z = c1 * c2 * s3 - s1 * s2 * c3;
  				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
  			} else if ( order === 'YZX' ) {
  
  				this._x = s1 * c2 * c3 + c1 * s2 * s3;
  				this._y = c1 * s2 * c3 + s1 * c2 * s3;
  				this._z = c1 * c2 * s3 - s1 * s2 * c3;
  				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
  			} else if ( order === 'XZY' ) {
  
  				this._x = s1 * c2 * c3 - c1 * s2 * s3;
  				this._y = c1 * s2 * c3 - s1 * c2 * s3;
  				this._z = c1 * c2 * s3 + s1 * s2 * c3;
  				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
  			}
  
  			if ( update !== false ) this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromAxisAngle: function ( axis, angle ) {
  
  			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
  
  			// assumes axis is normalized
  
  			var halfAngle = angle / 2, s = Math.sin( halfAngle );
  
  			this._x = axis.x * s;
  			this._y = axis.y * s;
  			this._z = axis.z * s;
  			this._w = Math.cos( halfAngle );
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromRotationMatrix: function ( m ) {
  
  			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
  
  			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
  			var te = m.elements,
  
  				m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
  				m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
  				m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
  
  				trace = m11 + m22 + m33,
  				s;
  
  			if ( trace > 0 ) {
  
  				s = 0.5 / Math.sqrt( trace + 1.0 );
  
  				this._w = 0.25 / s;
  				this._x = ( m32 - m23 ) * s;
  				this._y = ( m13 - m31 ) * s;
  				this._z = ( m21 - m12 ) * s;
  
  			} else if ( m11 > m22 && m11 > m33 ) {
  
  				s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
  
  				this._w = ( m32 - m23 ) / s;
  				this._x = 0.25 * s;
  				this._y = ( m12 + m21 ) / s;
  				this._z = ( m13 + m31 ) / s;
  
  			} else if ( m22 > m33 ) {
  
  				s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
  
  				this._w = ( m13 - m31 ) / s;
  				this._x = ( m12 + m21 ) / s;
  				this._y = 0.25 * s;
  				this._z = ( m23 + m32 ) / s;
  
  			} else {
  
  				s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
  
  				this._w = ( m21 - m12 ) / s;
  				this._x = ( m13 + m31 ) / s;
  				this._y = ( m23 + m32 ) / s;
  				this._z = 0.25 * s;
  
  			}
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromUnitVectors: function () {
  
  			// assumes direction vectors vFrom and vTo are normalized
  
  			var v1 = new Vector3();
  			var r;
  
  			var EPS = 0.000001;
  
  			return function setFromUnitVectors( vFrom, vTo ) {
  
  				if ( v1 === undefined ) v1 = new Vector3();
  
  				r = vFrom.dot( vTo ) + 1;
  
  				if ( r < EPS ) {
  
  					r = 0;
  
  					if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
  
  						v1.set( - vFrom.y, vFrom.x, 0 );
  
  					} else {
  
  						v1.set( 0, - vFrom.z, vFrom.y );
  
  					}
  
  				} else {
  
  					v1.crossVectors( vFrom, vTo );
  
  				}
  
  				this._x = v1.x;
  				this._y = v1.y;
  				this._z = v1.z;
  				this._w = r;
  
  				return this.normalize();
  
  			};
  
  		}(),
  
  		inverse: function () {
  
  			// quaternion is assumed to have unit length
  
  			return this.conjugate();
  
  		},
  
  		conjugate: function () {
  
  			this._x *= - 1;
  			this._y *= - 1;
  			this._z *= - 1;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		dot: function ( v ) {
  
  			return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
  
  		},
  
  		lengthSq: function () {
  
  			return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
  
  		},
  
  		length: function () {
  
  			return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
  
  		},
  
  		normalize: function () {
  
  			var l = this.length();
  
  			if ( l === 0 ) {
  
  				this._x = 0;
  				this._y = 0;
  				this._z = 0;
  				this._w = 1;
  
  			} else {
  
  				l = 1 / l;
  
  				this._x = this._x * l;
  				this._y = this._y * l;
  				this._z = this._z * l;
  				this._w = this._w * l;
  
  			}
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		multiply: function ( q, p ) {
  
  			if ( p !== undefined ) {
  
  				console.warn( 'Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
  				return this.multiplyQuaternions( q, p );
  
  			}
  
  			return this.multiplyQuaternions( this, q );
  
  		},
  
  		premultiply: function ( q ) {
  
  			return this.multiplyQuaternions( q, this );
  
  		},
  
  		multiplyQuaternions: function ( a, b ) {
  
  			// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
  
  			var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
  			var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
  
  			this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
  			this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
  			this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
  			this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		slerp: function ( qb, t ) {
  
  			if ( t === 0 ) return this;
  			if ( t === 1 ) return this.copy( qb );
  
  			var x = this._x, y = this._y, z = this._z, w = this._w;
  
  			// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
  
  			var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
  
  			if ( cosHalfTheta < 0 ) {
  
  				this._w = - qb._w;
  				this._x = - qb._x;
  				this._y = - qb._y;
  				this._z = - qb._z;
  
  				cosHalfTheta = - cosHalfTheta;
  
  			} else {
  
  				this.copy( qb );
  
  			}
  
  			if ( cosHalfTheta >= 1.0 ) {
  
  				this._w = w;
  				this._x = x;
  				this._y = y;
  				this._z = z;
  
  				return this;
  
  			}
  
  			var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
  
  			if ( Math.abs( sinHalfTheta ) < 0.001 ) {
  
  				this._w = 0.5 * ( w + this._w );
  				this._x = 0.5 * ( x + this._x );
  				this._y = 0.5 * ( y + this._y );
  				this._z = 0.5 * ( z + this._z );
  
  				return this;
  
  			}
  
  			var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
  			var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
  				ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
  
  			this._w = ( w * ratioA + this._w * ratioB );
  			this._x = ( x * ratioA + this._x * ratioB );
  			this._y = ( y * ratioA + this._y * ratioB );
  			this._z = ( z * ratioA + this._z * ratioB );
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		equals: function ( quaternion ) {
  
  			return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this._x = array[ offset ];
  			this._y = array[ offset + 1 ];
  			this._z = array[ offset + 2 ];
  			this._w = array[ offset + 3 ];
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this._x;
  			array[ offset + 1 ] = this._y;
  			array[ offset + 2 ] = this._z;
  			array[ offset + 3 ] = this._w;
  
  			return array;
  
  		},
  
  		onChange: function ( callback ) {
  
  			this.onChangeCallback = callback;
  
  			return this;
  
  		},
  
  		onChangeCallback: function () {}
  
  	} );
  
  	function Vector3( x, y, z ) {
  
  		this.x = x || 0;
  		this.y = y || 0;
  		this.z = z || 0;
  
  	}
  
  	Object.assign( Vector3.prototype, {
  
  		isVector3: true,
  
  		set: function ( x, y, z ) {
  
  			this.x = x;
  			this.y = y;
  			this.z = z;
  
  			return this;
  
  		},
  
  		setScalar: function ( scalar ) {
  
  			this.x = scalar;
  			this.y = scalar;
  			this.z = scalar;
  
  			return this;
  
  		},
  
  		setX: function ( x ) {
  
  			this.x = x;
  
  			return this;
  
  		},
  
  		setY: function ( y ) {
  
  			this.y = y;
  
  			return this;
  
  		},
  
  		setZ: function ( z ) {
  
  			this.z = z;
  
  			return this;
  
  		},
  
  		setComponent: function ( index, value ) {
  
  			switch ( index ) {
  
  				case 0: this.x = value; break;
  				case 1: this.y = value; break;
  				case 2: this.z = value; break;
  				default: throw new Error( 'index is out of range: ' + index );
  
  			}
  
  			return this;
  
  		},
  
  		getComponent: function ( index ) {
  
  			switch ( index ) {
  
  				case 0: return this.x;
  				case 1: return this.y;
  				case 2: return this.z;
  				default: throw new Error( 'index is out of range: ' + index );
  
  			}
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this.x, this.y, this.z );
  
  		},
  
  		copy: function ( v ) {
  
  			this.x = v.x;
  			this.y = v.y;
  			this.z = v.z;
  
  			return this;
  
  		},
  
  		add: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
  				return this.addVectors( v, w );
  
  			}
  
  			this.x += v.x;
  			this.y += v.y;
  			this.z += v.z;
  
  			return this;
  
  		},
  
  		addScalar: function ( s ) {
  
  			this.x += s;
  			this.y += s;
  			this.z += s;
  
  			return this;
  
  		},
  
  		addVectors: function ( a, b ) {
  
  			this.x = a.x + b.x;
  			this.y = a.y + b.y;
  			this.z = a.z + b.z;
  
  			return this;
  
  		},
  
  		addScaledVector: function ( v, s ) {
  
  			this.x += v.x * s;
  			this.y += v.y * s;
  			this.z += v.z * s;
  
  			return this;
  
  		},
  
  		sub: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
  				return this.subVectors( v, w );
  
  			}
  
  			this.x -= v.x;
  			this.y -= v.y;
  			this.z -= v.z;
  
  			return this;
  
  		},
  
  		subScalar: function ( s ) {
  
  			this.x -= s;
  			this.y -= s;
  			this.z -= s;
  
  			return this;
  
  		},
  
  		subVectors: function ( a, b ) {
  
  			this.x = a.x - b.x;
  			this.y = a.y - b.y;
  			this.z = a.z - b.z;
  
  			return this;
  
  		},
  
  		multiply: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
  				return this.multiplyVectors( v, w );
  
  			}
  
  			this.x *= v.x;
  			this.y *= v.y;
  			this.z *= v.z;
  
  			return this;
  
  		},
  
  		multiplyScalar: function ( scalar ) {
  
  			this.x *= scalar;
  			this.y *= scalar;
  			this.z *= scalar;
  
  			return this;
  
  		},
  
  		multiplyVectors: function ( a, b ) {
  
  			this.x = a.x * b.x;
  			this.y = a.y * b.y;
  			this.z = a.z * b.z;
  
  			return this;
  
  		},
  
  		applyEuler: function () {
  
  			var quaternion = new Quaternion();
  
  			return function applyEuler( euler ) {
  
  				if ( ! ( euler && euler.isEuler ) ) {
  
  					console.error( 'Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
  				}
  
  				return this.applyQuaternion( quaternion.setFromEuler( euler ) );
  
  			};
  
  		}(),
  
  		applyAxisAngle: function () {
  
  			var quaternion = new Quaternion();
  
  			return function applyAxisAngle( axis, angle ) {
  
  				return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
  
  			};
  
  		}(),
  
  		applyMatrix3: function ( m ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var e = m.elements;
  
  			this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
  			this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
  			this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
  
  			return this;
  
  		},
  
  		applyMatrix4: function ( m ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var e = m.elements;
  
  			var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
  
  			this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
  			this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
  			this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
  
  			return this;
  
  		},
  
  		applyQuaternion: function ( q ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var qx = q.x, qy = q.y, qz = q.z, qw = q.w;
  
  			// calculate quat * vector
  
  			var ix = qw * x + qy * z - qz * y;
  			var iy = qw * y + qz * x - qx * z;
  			var iz = qw * z + qx * y - qy * x;
  			var iw = - qx * x - qy * y - qz * z;
  
  			// calculate result * inverse quat
  
  			this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
  			this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
  			this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
  
  			return this;
  
  		},
  
  		project: function () {
  
  			var matrix = new Matrix4();
  
  			return function project( camera ) {
  
  				matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
  				return this.applyMatrix4( matrix );
  
  			};
  
  		}(),
  
  		unproject: function () {
  
  			var matrix = new Matrix4();
  
  			return function unproject( camera ) {
  
  				matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
  				return this.applyMatrix4( matrix );
  
  			};
  
  		}(),
  
  		transformDirection: function ( m ) {
  
  			// input: Matrix4 affine matrix
  			// vector interpreted as a direction
  
  			var x = this.x, y = this.y, z = this.z;
  			var e = m.elements;
  
  			this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
  			this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
  			this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
  
  			return this.normalize();
  
  		},
  
  		divide: function ( v ) {
  
  			this.x /= v.x;
  			this.y /= v.y;
  			this.z /= v.z;
  
  			return this;
  
  		},
  
  		divideScalar: function ( scalar ) {
  
  			return this.multiplyScalar( 1 / scalar );
  
  		},
  
  		min: function ( v ) {
  
  			this.x = Math.min( this.x, v.x );
  			this.y = Math.min( this.y, v.y );
  			this.z = Math.min( this.z, v.z );
  
  			return this;
  
  		},
  
  		max: function ( v ) {
  
  			this.x = Math.max( this.x, v.x );
  			this.y = Math.max( this.y, v.y );
  			this.z = Math.max( this.z, v.z );
  
  			return this;
  
  		},
  
  		clamp: function ( min, max ) {
  
  			// assumes min < max, componentwise
  
  			this.x = Math.max( min.x, Math.min( max.x, this.x ) );
  			this.y = Math.max( min.y, Math.min( max.y, this.y ) );
  			this.z = Math.max( min.z, Math.min( max.z, this.z ) );
  
  			return this;
  
  		},
  
  		clampScalar: function () {
  
  			var min = new Vector3();
  			var max = new Vector3();
  
  			return function clampScalar( minVal, maxVal ) {
  
  				min.set( minVal, minVal, minVal );
  				max.set( maxVal, maxVal, maxVal );
  
  				return this.clamp( min, max );
  
  			};
  
  		}(),
  
  		clampLength: function ( min, max ) {
  
  			var length = this.length();
  
  			return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
  
  		},
  
  		floor: function () {
  
  			this.x = Math.floor( this.x );
  			this.y = Math.floor( this.y );
  			this.z = Math.floor( this.z );
  
  			return this;
  
  		},
  
  		ceil: function () {
  
  			this.x = Math.ceil( this.x );
  			this.y = Math.ceil( this.y );
  			this.z = Math.ceil( this.z );
  
  			return this;
  
  		},
  
  		round: function () {
  
  			this.x = Math.round( this.x );
  			this.y = Math.round( this.y );
  			this.z = Math.round( this.z );
  
  			return this;
  
  		},
  
  		roundToZero: function () {
  
  			this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
  			this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
  			this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
  
  			return this;
  
  		},
  
  		negate: function () {
  
  			this.x = - this.x;
  			this.y = - this.y;
  			this.z = - this.z;
  
  			return this;
  
  		},
  
  		dot: function ( v ) {
  
  			return this.x * v.x + this.y * v.y + this.z * v.z;
  
  		},
  
  		// TODO lengthSquared?
  
  		lengthSq: function () {
  
  			return this.x * this.x + this.y * this.y + this.z * this.z;
  
  		},
  
  		length: function () {
  
  			return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
  
  		},
  
  		manhattanLength: function () {
  
  			return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
  
  		},
  
  		normalize: function () {
  
  			return this.divideScalar( this.length() || 1 );
  
  		},
  
  		setLength: function ( length ) {
  
  			return this.normalize().multiplyScalar( length );
  
  		},
  
  		lerp: function ( v, alpha ) {
  
  			this.x += ( v.x - this.x ) * alpha;
  			this.y += ( v.y - this.y ) * alpha;
  			this.z += ( v.z - this.z ) * alpha;
  
  			return this;
  
  		},
  
  		lerpVectors: function ( v1, v2, alpha ) {
  
  			return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
  
  		},
  
  		cross: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
  				return this.crossVectors( v, w );
  
  			}
  
  			return this.crossVectors( this, v );
  
  		},
  
  		crossVectors: function ( a, b ) {
  
  			var ax = a.x, ay = a.y, az = a.z;
  			var bx = b.x, by = b.y, bz = b.z;
  
  			this.x = ay * bz - az * by;
  			this.y = az * bx - ax * bz;
  			this.z = ax * by - ay * bx;
  
  			return this;
  
  		},
  
  		projectOnVector: function ( vector ) {
  
  			var scalar = vector.dot( this ) / vector.lengthSq();
  
  			return this.copy( vector ).multiplyScalar( scalar );
  
  		},
  
  		projectOnPlane: function () {
  
  			var v1 = new Vector3();
  
  			return function projectOnPlane( planeNormal ) {
  
  				v1.copy( this ).projectOnVector( planeNormal );
  
  				return this.sub( v1 );
  
  			};
  
  		}(),
  
  		reflect: function () {
  
  			// reflect incident vector off plane orthogonal to normal
  			// normal is assumed to have unit length
  
  			var v1 = new Vector3();
  
  			return function reflect( normal ) {
  
  				return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
  
  			};
  
  		}(),
  
  		angleTo: function ( v ) {
  
  			var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
  
  			// clamp, to handle numerical problems
  
  			return Math.acos( _Math.clamp( theta, - 1, 1 ) );
  
  		},
  
  		distanceTo: function ( v ) {
  
  			return Math.sqrt( this.distanceToSquared( v ) );
  
  		},
  
  		distanceToSquared: function ( v ) {
  
  			var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
  
  			return dx * dx + dy * dy + dz * dz;
  
  		},
  
  		manhattanDistanceTo: function ( v ) {
  
  			return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
  
  		},
  
  		setFromSpherical: function ( s ) {
  
  			var sinPhiRadius = Math.sin( s.phi ) * s.radius;
  
  			this.x = sinPhiRadius * Math.sin( s.theta );
  			this.y = Math.cos( s.phi ) * s.radius;
  			this.z = sinPhiRadius * Math.cos( s.theta );
  
  			return this;
  
  		},
  
  		setFromCylindrical: function ( c ) {
  
  			this.x = c.radius * Math.sin( c.theta );
  			this.y = c.y;
  			this.z = c.radius * Math.cos( c.theta );
  
  			return this;
  
  		},
  
  		setFromMatrixPosition: function ( m ) {
  
  			var e = m.elements;
  
  			this.x = e[ 12 ];
  			this.y = e[ 13 ];
  			this.z = e[ 14 ];
  
  			return this;
  
  		},
  
  		setFromMatrixScale: function ( m ) {
  
  			var sx = this.setFromMatrixColumn( m, 0 ).length();
  			var sy = this.setFromMatrixColumn( m, 1 ).length();
  			var sz = this.setFromMatrixColumn( m, 2 ).length();
  
  			this.x = sx;
  			this.y = sy;
  			this.z = sz;
  
  			return this;
  
  		},
  
  		setFromMatrixColumn: function ( m, index ) {
  
  			return this.fromArray( m.elements, index * 4 );
  
  		},
  
  		equals: function ( v ) {
  
  			return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this.x = array[ offset ];
  			this.y = array[ offset + 1 ];
  			this.z = array[ offset + 2 ];
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this.x;
  			array[ offset + 1 ] = this.y;
  			array[ offset + 2 ] = this.z;
  
  			return array;
  
  		},
  
  		fromBufferAttribute: function ( attribute, index, offset ) {
  
  			if ( offset !== undefined ) {
  
  				console.warn( 'Vector3: offset has been removed from .fromBufferAttribute().' );
  
  			}
  
  			this.x = attribute.getX( index );
  			this.y = attribute.getY( index );
  			this.z = attribute.getZ( index );
  
  			return this;
  
  		}
  
  	} );
  
  	function Vector2( x, y ) {
  
  		this.x = x || 0;
  		this.y = y || 0;
  
  	}
  
  	Object.defineProperties( Vector2.prototype, {
  
  		"width": {
  
  			get: function () {
  
  				return this.x;
  
  			},
  
  			set: function ( value ) {
  
  				this.x = value;
  
  			}
  
  		},
  
  		"height": {
  
  			get: function () {
  
  				return this.y;
  
  			},
  
  			set: function ( value ) {
  
  				this.y = value;
  
  			}
  
  		}
  
  	} );
  
  	Object.assign( Vector2.prototype, {
  
  		isVector2: true,
  
  		set: function ( x, y ) {
  
  			this.x = x;
  			this.y = y;
  
  			return this;
  
  		},
  
  		setScalar: function ( scalar ) {
  
  			this.x = scalar;
  			this.y = scalar;
  
  			return this;
  
  		},
  
  		setX: function ( x ) {
  
  			this.x = x;
  
  			return this;
  
  		},
  
  		setY: function ( y ) {
  
  			this.y = y;
  
  			return this;
  
  		},
  
  		setComponent: function ( index, value ) {
  
  			switch ( index ) {
  
  				case 0: this.x = value; break;
  				case 1: this.y = value; break;
  				default: throw new Error( 'index is out of range: ' + index );
  
  			}
  
  			return this;
  
  		},
  
  		getComponent: function ( index ) {
  
  			switch ( index ) {
  
  				case 0: return this.x;
  				case 1: return this.y;
  				default: throw new Error( 'index is out of range: ' + index );
  
  			}
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this.x, this.y );
  
  		},
  
  		copy: function ( v ) {
  
  			this.x = v.x;
  			this.y = v.y;
  
  			return this;
  
  		},
  
  		add: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
  				return this.addVectors( v, w );
  
  			}
  
  			this.x += v.x;
  			this.y += v.y;
  
  			return this;
  
  		},
  
  		addScalar: function ( s ) {
  
  			this.x += s;
  			this.y += s;
  
  			return this;
  
  		},
  
  		addVectors: function ( a, b ) {
  
  			this.x = a.x + b.x;
  			this.y = a.y + b.y;
  
  			return this;
  
  		},
  
  		addScaledVector: function ( v, s ) {
  
  			this.x += v.x * s;
  			this.y += v.y * s;
  
  			return this;
  
  		},
  
  		sub: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
  				return this.subVectors( v, w );
  
  			}
  
  			this.x -= v.x;
  			this.y -= v.y;
  
  			return this;
  
  		},
  
  		subScalar: function ( s ) {
  
  			this.x -= s;
  			this.y -= s;
  
  			return this;
  
  		},
  
  		subVectors: function ( a, b ) {
  
  			this.x = a.x - b.x;
  			this.y = a.y - b.y;
  
  			return this;
  
  		},
  
  		multiply: function ( v ) {
  
  			this.x *= v.x;
  			this.y *= v.y;
  
  			return this;
  
  		},
  
  		multiplyScalar: function ( scalar ) {
  
  			this.x *= scalar;
  			this.y *= scalar;
  
  			return this;
  
  		},
  
  		divide: function ( v ) {
  
  			this.x /= v.x;
  			this.y /= v.y;
  
  			return this;
  
  		},
  
  		divideScalar: function ( scalar ) {
  
  			return this.multiplyScalar( 1 / scalar );
  
  		},
  
  		applyMatrix3: function ( m ) {
  
  			var x = this.x, y = this.y;
  			var e = m.elements;
  
  			this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];
  			this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];
  
  			return this;
  
  		},
  
  		min: function ( v ) {
  
  			this.x = Math.min( this.x, v.x );
  			this.y = Math.min( this.y, v.y );
  
  			return this;
  
  		},
  
  		max: function ( v ) {
  
  			this.x = Math.max( this.x, v.x );
  			this.y = Math.max( this.y, v.y );
  
  			return this;
  
  		},
  
  		clamp: function ( min, max ) {
  
  			// assumes min < max, componentwise
  
  			this.x = Math.max( min.x, Math.min( max.x, this.x ) );
  			this.y = Math.max( min.y, Math.min( max.y, this.y ) );
  
  			return this;
  
  		},
  
  		clampScalar: function () {
  
  			var min = new Vector2();
  			var max = new Vector2();
  
  			return function clampScalar( minVal, maxVal ) {
  
  				min.set( minVal, minVal );
  				max.set( maxVal, maxVal );
  
  				return this.clamp( min, max );
  
  			};
  
  		}(),
  
  		clampLength: function ( min, max ) {
  
  			var length = this.length();
  
  			return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
  
  		},
  
  		floor: function () {
  
  			this.x = Math.floor( this.x );
  			this.y = Math.floor( this.y );
  
  			return this;
  
  		},
  
  		ceil: function () {
  
  			this.x = Math.ceil( this.x );
  			this.y = Math.ceil( this.y );
  
  			return this;
  
  		},
  
  		round: function () {
  
  			this.x = Math.round( this.x );
  			this.y = Math.round( this.y );
  
  			return this;
  
  		},
  
  		roundToZero: function () {
  
  			this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
  			this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
  
  			return this;
  
  		},
  
  		negate: function () {
  
  			this.x = - this.x;
  			this.y = - this.y;
  
  			return this;
  
  		},
  
  		dot: function ( v ) {
  
  			return this.x * v.x + this.y * v.y;
  
  		},
  
  		lengthSq: function () {
  
  			return this.x * this.x + this.y * this.y;
  
  		},
  
  		length: function () {
  
  			return Math.sqrt( this.x * this.x + this.y * this.y );
  
  		},
  
  		manhattanLength: function () {
  
  			return Math.abs( this.x ) + Math.abs( this.y );
  
  		},
  
  		normalize: function () {
  
  			return this.divideScalar( this.length() || 1 );
  
  		},
  
  		angle: function () {
  
  			// computes the angle in radians with respect to the positive x-axis
  
  			var angle = Math.atan2( this.y, this.x );
  
  			if ( angle < 0 ) angle += 2 * Math.PI;
  
  			return angle;
  
  		},
  
  		distanceTo: function ( v ) {
  
  			return Math.sqrt( this.distanceToSquared( v ) );
  
  		},
  
  		distanceToSquared: function ( v ) {
  
  			var dx = this.x - v.x, dy = this.y - v.y;
  			return dx * dx + dy * dy;
  
  		},
  
  		manhattanDistanceTo: function ( v ) {
  
  			return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
  
  		},
  
  		setLength: function ( length ) {
  
  			return this.normalize().multiplyScalar( length );
  
  		},
  
  		lerp: function ( v, alpha ) {
  
  			this.x += ( v.x - this.x ) * alpha;
  			this.y += ( v.y - this.y ) * alpha;
  
  			return this;
  
  		},
  
  		lerpVectors: function ( v1, v2, alpha ) {
  
  			return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
  
  		},
  
  		equals: function ( v ) {
  
  			return ( ( v.x === this.x ) && ( v.y === this.y ) );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this.x = array[ offset ];
  			this.y = array[ offset + 1 ];
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this.x;
  			array[ offset + 1 ] = this.y;
  
  			return array;
  
  		},
  
  		fromBufferAttribute: function ( attribute, index, offset ) {
  
  			if ( offset !== undefined ) {
  
  				console.warn( 'Vector2: offset has been removed from .fromBufferAttribute().' );
  
  			}
  
  			this.x = attribute.getX( index );
  			this.y = attribute.getY( index );
  
  			return this;
  
  		},
  
  		rotateAround: function ( center, angle ) {
  
  			var c = Math.cos( angle ), s = Math.sin( angle );
  
  			var x = this.x - center.x;
  			var y = this.y - center.y;
  
  			this.x = x * c - y * s + center.x;
  			this.y = x * s + y * c + center.y;
  
  			return this;
  
  		}
  
  	} );
  
  	function Matrix3() {
  
  		this.elements = [
  
  			1, 0, 0,
  			0, 1, 0,
  			0, 0, 1
  
  		];
  
  		if ( arguments.length > 0 ) {
  
  			console.error( 'Matrix3: the constructor no longer reads arguments. use .set() instead.' );
  
  		}
  
  	}
  
  	Object.assign( Matrix3.prototype, {
  
  		isMatrix3: true,
  
  		set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
  
  			var te = this.elements;
  
  			te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
  			te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
  			te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
  
  			return this;
  
  		},
  
  		identity: function () {
  
  			this.set(
  
  				1, 0, 0,
  				0, 1, 0,
  				0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor().fromArray( this.elements );
  
  		},
  
  		copy: function ( m ) {
  
  			var te = this.elements;
  			var me = m.elements;
  
  			te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
  			te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
  			te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
  
  			return this;
  
  		},
  
  		setFromMatrix4: function ( m ) {
  
  			var me = m.elements;
  
  			this.set(
  
  				me[ 0 ], me[ 4 ], me[ 8 ],
  				me[ 1 ], me[ 5 ], me[ 9 ],
  				me[ 2 ], me[ 6 ], me[ 10 ]
  
  			);
  
  			return this;
  
  		},
  
  		applyToBufferAttribute: function () {
  
  			var v1 = new Vector3();
  
  			return function applyToBufferAttribute( attribute ) {
  
  				for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
  					v1.x = attribute.getX( i );
  					v1.y = attribute.getY( i );
  					v1.z = attribute.getZ( i );
  
  					v1.applyMatrix3( this );
  
  					attribute.setXYZ( i, v1.x, v1.y, v1.z );
  
  				}
  
  				return attribute;
  
  			};
  
  		}(),
  
  		multiply: function ( m ) {
  
  			return this.multiplyMatrices( this, m );
  
  		},
  
  		premultiply: function ( m ) {
  
  			return this.multiplyMatrices( m, this );
  
  		},
  
  		multiplyMatrices: function ( a, b ) {
  
  			var ae = a.elements;
  			var be = b.elements;
  			var te = this.elements;
  
  			var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
  			var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
  			var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
  
  			var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
  			var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
  			var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
  
  			te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
  			te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
  			te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
  
  			te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
  			te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
  			te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
  
  			te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
  			te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
  			te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
  
  			return this;
  
  		},
  
  		multiplyScalar: function ( s ) {
  
  			var te = this.elements;
  
  			te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
  			te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
  			te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
  
  			return this;
  
  		},
  
  		determinant: function () {
  
  			var te = this.elements;
  
  			var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
  				d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
  				g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
  
  			return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
  
  		},
  
  		getInverse: function ( matrix, throwOnDegenerate ) {
  
  			if ( matrix && matrix.isMatrix4 ) {
  
  				console.error( "Matrix3: .getInverse() no longer takes a Matrix4 argument." );
  
  			}
  
  			var me = matrix.elements,
  				te = this.elements,
  
  				n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
  				n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
  				n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],
  
  				t11 = n33 * n22 - n32 * n23,
  				t12 = n32 * n13 - n33 * n12,
  				t13 = n23 * n12 - n22 * n13,
  
  				det = n11 * t11 + n21 * t12 + n31 * t13;
  
  			if ( det === 0 ) {
  
  				var msg = "Matrix3: .getInverse() can't invert matrix, determinant is 0";
  
  				if ( throwOnDegenerate === true ) {
  
  					throw new Error( msg );
  
  				} else {
  
  					console.warn( msg );
  
  				}
  
  				return this.identity();
  
  			}
  
  			var detInv = 1 / det;
  
  			te[ 0 ] = t11 * detInv;
  			te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
  			te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
  
  			te[ 3 ] = t12 * detInv;
  			te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
  			te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
  
  			te[ 6 ] = t13 * detInv;
  			te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
  			te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
  
  			return this;
  
  		},
  
  		transpose: function () {
  
  			var tmp, m = this.elements;
  
  			tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
  			tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
  			tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
  
  			return this;
  
  		},
  
  		getNormalMatrix: function ( matrix4 ) {
  
  			return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
  
  		},
  
  		transposeIntoArray: function ( r ) {
  
  			var m = this.elements;
  
  			r[ 0 ] = m[ 0 ];
  			r[ 1 ] = m[ 3 ];
  			r[ 2 ] = m[ 6 ];
  			r[ 3 ] = m[ 1 ];
  			r[ 4 ] = m[ 4 ];
  			r[ 5 ] = m[ 7 ];
  			r[ 6 ] = m[ 2 ];
  			r[ 7 ] = m[ 5 ];
  			r[ 8 ] = m[ 8 ];
  
  			return this;
  
  		},
  
  		setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) {
  
  			var c = Math.cos( rotation );
  			var s = Math.sin( rotation );
  
  			this.set(
  				sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
  				- sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
  				0, 0, 1
  			);
  
  		},
  
  		scale: function ( sx, sy ) {
  
  			var te = this.elements;
  
  			te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;
  			te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;
  
  			return this;
  
  		},
  
  		rotate: function ( theta ) {
  
  			var c = Math.cos( theta );
  			var s = Math.sin( theta );
  
  			var te = this.elements;
  
  			var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];
  			var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];
  
  			te[ 0 ] = c * a11 + s * a21;
  			te[ 3 ] = c * a12 + s * a22;
  			te[ 6 ] = c * a13 + s * a23;
  
  			te[ 1 ] = - s * a11 + c * a21;
  			te[ 4 ] = - s * a12 + c * a22;
  			te[ 7 ] = - s * a13 + c * a23;
  
  			return this;
  
  		},
  
  		translate: function ( tx, ty ) {
  
  			var te = this.elements;
  
  			te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];
  			te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];
  
  			return this;
  
  		},
  
  		equals: function ( matrix ) {
  
  			var te = this.elements;
  			var me = matrix.elements;
  
  			for ( var i = 0; i < 9; i ++ ) {
  
  				if ( te[ i ] !== me[ i ] ) return false;
  
  			}
  
  			return true;
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			for ( var i = 0; i < 9; i ++ ) {
  
  				this.elements[ i ] = array[ i + offset ];
  
  			}
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			var te = this.elements;
  
  			array[ offset ] = te[ 0 ];
  			array[ offset + 1 ] = te[ 1 ];
  			array[ offset + 2 ] = te[ 2 ];
  
  			array[ offset + 3 ] = te[ 3 ];
  			array[ offset + 4 ] = te[ 4 ];
  			array[ offset + 5 ] = te[ 5 ];
  
  			array[ offset + 6 ] = te[ 6 ];
  			array[ offset + 7 ] = te[ 7 ];
  			array[ offset + 8 ] = te[ 8 ];
  
  			return array;
  
  		}
  
  	} );
  
  	var UniformsLib = {
  
  		common: {
  
  			diffuse: { value: new Color( 0xeeeeee ) },
  			opacity: { value: 1.0 },
  
  			map: { value: null },
  			uvTransform: { value: new Matrix3() },
  
  			alphaMap: { value: null },
  
  		},
  
  		specularmap: {
  
  			specularMap: { value: null },
  
  		},
  
  		envmap: {
  
  			envMap: { value: null },
  			flipEnvMap: { value: - 1 },
  			reflectivity: { value: 1.0 },
  			refractionRatio: { value: 0.98 }
  
  		},
  
  		aomap: {
  
  			aoMap: { value: null },
  			aoMapIntensity: { value: 1 }
  
  		},
  
  		lightmap: {
  
  			lightMap: { value: null },
  			lightMapIntensity: { value: 1 }
  
  		},
  
  		emissivemap: {
  
  			emissiveMap: { value: null }
  
  		},
  
  		bumpmap: {
  
  			bumpMap: { value: null },
  			bumpScale: { value: 1 }
  
  		},
  
  		normalmap: {
  
  			normalMap: { value: null },
  			normalScale: { value: new Vector2( 1, 1 ) }
  
  		},
  
  		displacementmap: {
  
  			displacementMap: { value: null },
  			displacementScale: { value: 1 },
  			displacementBias: { value: 0 }
  
  		},
  
  		roughnessmap: {
  
  			roughnessMap: { value: null }
  
  		},
  
  		metalnessmap: {
  
  			metalnessMap: { value: null }
  
  		},
  
  		gradientmap: {
  
  			gradientMap: { value: null }
  
  		},
  
  		fog: {
  
  			fogDensity: { value: 0.00025 },
  			fogNear: { value: 1 },
  			fogFar: { value: 2000 },
  			fogColor: { value: new Color( 0xffffff ) }
  
  		},
  
  		lights: {
  
  			ambientLightColor: { value: [] },
  
  			directionalLights: { value: [], properties: {
  				direction: {},
  				color: {},
  
  				shadow: {},
  				shadowBias: {},
  				shadowRadius: {},
  				shadowMapSize: {}
  			} },
  
  			directionalShadowMap: { value: [] },
  			directionalShadowMatrix: { value: [] },
  
  			spotLights: { value: [], properties: {
  				color: {},
  				position: {},
  				direction: {},
  				distance: {},
  				coneCos: {},
  				penumbraCos: {},
  				decay: {},
  
  				shadow: {},
  				shadowBias: {},
  				shadowRadius: {},
  				shadowMapSize: {}
  			} },
  
  			spotShadowMap: { value: [] },
  			spotShadowMatrix: { value: [] },
  
  			pointLights: { value: [], properties: {
  				color: {},
  				position: {},
  				decay: {},
  				distance: {},
  
  				shadow: {},
  				shadowBias: {},
  				shadowRadius: {},
  				shadowMapSize: {},
  				shadowCameraNear: {},
  				shadowCameraFar: {}
  			} },
  
  			pointShadowMap: { value: [] },
  			pointShadowMatrix: { value: [] },
  
  			hemisphereLights: { value: [], properties: {
  				direction: {},
  				skyColor: {},
  				groundColor: {}
  			} },
  
  			// TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src
  			rectAreaLights: { value: [], properties: {
  				color: {},
  				position: {},
  				width: {},
  				height: {}
  			} }
  
  		},
  
  		points: {
  
  			diffuse: { value: new Color( 0xeeeeee ) },
  			opacity: { value: 1.0 },
  			size: { value: 1.0 },
  			scale: { value: 1.0 },
  			map: { value: null },
  			uvTransform: { value: new Matrix3() }
  
  		}
  
  	};
  
  	var ShaderLib = {
  
  		basic: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.specularmap,
  				UniformsLib.envmap,
  				UniformsLib.aomap,
  				UniformsLib.lightmap,
  				UniformsLib.fog
  			] ),
  
  			vertexShader: ShaderChunk.meshbasic_vert,
  			fragmentShader: ShaderChunk.meshbasic_frag
  
  		},
  
  		lambert: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.specularmap,
  				UniformsLib.envmap,
  				UniformsLib.aomap,
  				UniformsLib.lightmap,
  				UniformsLib.emissivemap,
  				UniformsLib.fog,
  				UniformsLib.lights,
  				{
  					emissive: { value: new Color( 0x000000 ) }
  				}
  			] ),
  
  			vertexShader: ShaderChunk.meshlambert_vert,
  			fragmentShader: ShaderChunk.meshlambert_frag
  
  		},
  
  		phong: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.specularmap,
  				UniformsLib.envmap,
  				UniformsLib.aomap,
  				UniformsLib.lightmap,
  				UniformsLib.emissivemap,
  				UniformsLib.bumpmap,
  				UniformsLib.normalmap,
  				UniformsLib.displacementmap,
  				UniformsLib.gradientmap,
  				UniformsLib.fog,
  				UniformsLib.lights,
  				{
  					emissive: { value: new Color( 0x000000 ) },
  					specular: { value: new Color( 0x111111 ) },
  					shininess: { value: 30 }
  				}
  			] ),
  
  			vertexShader: ShaderChunk.meshphong_vert,
  			fragmentShader: ShaderChunk.meshphong_frag
  
  		},
  
  		standard: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.envmap,
  				UniformsLib.aomap,
  				UniformsLib.lightmap,
  				UniformsLib.emissivemap,
  				UniformsLib.bumpmap,
  				UniformsLib.normalmap,
  				UniformsLib.displacementmap,
  				UniformsLib.roughnessmap,
  				UniformsLib.metalnessmap,
  				UniformsLib.fog,
  				UniformsLib.lights,
  				{
  					emissive: { value: new Color( 0x000000 ) },
  					roughness: { value: 0.5 },
  					metalness: { value: 0.5 },
  					envMapIntensity: { value: 1 } // temporary
  				}
  			] ),
  
  			vertexShader: ShaderChunk.meshphysical_vert,
  			fragmentShader: ShaderChunk.meshphysical_frag
  
  		},
  
  		points: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.points,
  				UniformsLib.fog
  			] ),
  
  			vertexShader: ShaderChunk.points_vert,
  			fragmentShader: ShaderChunk.points_frag
  
  		},
  
  		dashed: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.fog,
  				{
  					scale: { value: 1 },
  					dashSize: { value: 1 },
  					totalSize: { value: 2 }
  				}
  			] ),
  
  			vertexShader: ShaderChunk.linedashed_vert,
  			fragmentShader: ShaderChunk.linedashed_frag
  
  		},
  
  		depth: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.displacementmap
  			] ),
  
  			vertexShader: ShaderChunk.depth_vert,
  			fragmentShader: ShaderChunk.depth_frag
  
  		},
  
  		normal: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.bumpmap,
  				UniformsLib.normalmap,
  				UniformsLib.displacementmap,
  				{
  					opacity: { value: 1.0 }
  				}
  			] ),
  
  			vertexShader: ShaderChunk.normal_vert,
  			fragmentShader: ShaderChunk.normal_frag
  
  		},
  
  		
  
  		cube: {
  
  			uniforms: {
  				tCube: { value: null },
  				tFlip: { value: - 1 },
  				opacity: { value: 1.0 }
  			},
  
  			vertexShader: ShaderChunk.cube_vert,
  			fragmentShader: ShaderChunk.cube_frag
  
  		},
  
  		equirect: {
  
  			uniforms: {
  				tEquirect: { value: null },
  			},
  
  			vertexShader: ShaderChunk.equirect_vert,
  			fragmentShader: ShaderChunk.equirect_frag
  
  		},
  
  		distanceRGBA: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.common,
  				UniformsLib.displacementmap,
  				{
  					referencePosition: { value: new Vector3() },
  					nearDistance: { value: 1 },
  					farDistance: { value: 1000 }
  				}
  			] ),
  
  			vertexShader: ShaderChunk.distanceRGBA_vert,
  			fragmentShader: ShaderChunk.distanceRGBA_frag
  
  		},
  
  		shadow: {
  
  			uniforms: UniformsUtils.merge( [
  				UniformsLib.lights,
  				UniformsLib.fog,
  				{
  					color: { value: new Color( 0x00000 ) },
  					opacity: { value: 1.0 }
  				},
  			] ),
  
  			vertexShader: ShaderChunk.shadow_vert,
  			fragmentShader: ShaderChunk.shadow_frag
  
  		}
  
  	};
  
  	ShaderLib.physical = {
  
  		uniforms: UniformsUtils.merge( [
  			ShaderLib.standard.uniforms,
  			{
  				clearCoat: { value: 0 },
  				clearCoatRoughness: { value: 0 }
  			}
  		] ),
  
  		vertexShader: ShaderChunk.meshphysical_vert,
  		fragmentShader: ShaderChunk.meshphysical_frag
  
  	};
  
  	var OutlineEffect = function ( renderer, parameters ) {
  
  		parameters = parameters || {};
  
  		this.enabled = true;
  
  		var defaultThickness = parameters.defaultThickness !== undefined ? parameters.defaultThickness : 0.003;
  		var defaultColor = parameters.defaultColor !== undefined ? parameters.defaultColor : new Color( 0x000000 );
  		var defaultAlpha = parameters.defaultAlpha !== undefined ? parameters.defaultAlpha : 1.0;
  		var defaultKeepAlive = parameters.defaultKeepAlive !== undefined ? parameters.defaultKeepAlive : false;
  
  		// object.material.uuid -> outlineMaterial or
  		// object.material[ n ].uuid -> outlineMaterial
  		// save at the outline material creation and release
  		// if it's unused removeThresholdCount frames
  		// unless keepAlive is true.
  		var cache = {};
  
  		var removeThresholdCount = 60;
  
  		// outlineMaterial.uuid -> object.material or
  		// outlineMaterial.uuid -> object.material[ n ]
  		// save before render and release after render.
  		var originalMaterials = {};
  
  		// object.uuid -> originalOnBeforeRender
  		// save before render and release after render.
  		var originalOnBeforeRenders = {};
  
  		//this.cache = cache;  // for debug
  
  		// copied from WebGLPrograms and removed some materials
  		var shaderIDs = {
  			MeshBasicMaterial: 'basic',
  			MeshLambertMaterial: 'lambert',
  			MeshPhongMaterial: 'phong',
  			MeshToonMaterial: 'phong',
  			MeshStandardMaterial: 'physical',
  			MeshPhysicalMaterial: 'physical'
  		};
  
  		var uniformsChunk = {
  			outlineThickness: { type: "f", value: defaultThickness },
  			outlineColor: { type: "c", value: defaultColor },
  			outlineAlpha: { type: "f", value: defaultAlpha }
  		};
  
  		var vertexShaderChunk = [
  
  			"#include <fog_pars_vertex>",
  
  			"uniform float outlineThickness;",
  
  			"vec4 calculateOutline( vec4 pos, vec3 objectNormal, vec4 skinned ) {",
  
  			"	float thickness = outlineThickness;",
  			"	const float ratio = 1.0;", // TODO: support outline thickness ratio for each vertex
  			"	vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + objectNormal, 1.0 );",
  			// NOTE: subtract pos2 from pos because BackSide objectNormal is negative
  			"	vec4 norm = normalize( pos - pos2 );",
  			"	return pos + norm * thickness * pos.w * ratio;",
  
  			"}"
  
  		].join( "\n" );
  
  		var vertexShaderChunk2 = [
  
  			"#if ! defined( LAMBERT ) && ! defined( PHONG ) && ! defined( TOON ) && ! defined( PHYSICAL )",
  			"	#ifndef USE_ENVMAP",
  			"		vec3 objectNormal = normalize( normal );",
  			"	#endif",
  			"#endif",
  
  			"#ifdef FLIP_SIDED",
  			"	objectNormal = -objectNormal;",
  			"#endif",
  
  			"#ifdef DECLARE_TRANSFORMED",
  			"	vec3 transformed = vec3( position );",
  			"#endif",
  
  			"gl_Position = calculateOutline( gl_Position, objectNormal, vec4( transformed, 1.0 ) );",
  
  			"#include <fog_vertex>"
  
  		].join( "\n" );
  
  		var fragmentShader = [
  
  			"#include <common>",
  			"#include <fog_pars_fragment>",
  
  			"uniform vec3 outlineColor;",
  			"uniform float outlineAlpha;",
  
  			"void main() {",
  
  			"	gl_FragColor = vec4( outlineColor, outlineAlpha );",
  
  			"	#include <fog_fragment>",
  
  			"}"
  
  		].join( "\n" );
  
  		function createInvisibleMaterial() {
  
  			return new ShaderMaterial( { name: 'invisible', visible: false } );
  
  		}
  
  		function createMaterial( originalMaterial ) {
  
  			var shaderID = shaderIDs[ originalMaterial.type ];
  			var originalUniforms, originalVertexShader;
  			var outlineParameters = originalMaterial.outlineParameters;
  
  			if ( shaderID !== undefined ) {
  
  				var shader = ShaderLib[ shaderID ];
  				originalUniforms = shader.uniforms;
  				originalVertexShader = shader.vertexShader;
  
  			} else if ( originalMaterial.isRawShaderMaterial === true ) {
  
  				originalUniforms = originalMaterial.uniforms;
  				originalVertexShader = originalMaterial.vertexShader;
  
  				if ( ! /attribute\s+vec3\s+position\s*;/.test( originalVertexShader ) ||
  				     ! /attribute\s+vec3\s+normal\s*;/.test( originalVertexShader ) ) {
  
  					console.warn( 'OutlineEffect requires both vec3 position and normal attributes in vertex shader, ' +
  					              'does not draw outline for ' + originalMaterial.name + '(uuid:' + originalMaterial.uuid + ') material.' );
  
  					return createInvisibleMaterial();
  
  				}
  
  			} else if ( originalMaterial.isShaderMaterial === true ) {
  
  				originalUniforms = originalMaterial.uniforms;
  				originalVertexShader = originalMaterial.vertexShader;
  
  			} else {
  
  				return createInvisibleMaterial();
  
  			}
  
  			var uniforms = Object.assign( {}, originalUniforms, uniformsChunk );
  
  			var vertexShader = originalVertexShader
  						// put vertexShaderChunk right before "void main() {...}"
  						.replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' )
  						// put vertexShaderChunk2 the end of "void main() {...}"
  						// Note: here assums originalVertexShader ends with "}" of "void main() {...}"
  						.replace( /\}\s*$/, vertexShaderChunk2 + '\n}' )
  						// remove any light related lines
  						// Note: here is very sensitive to originalVertexShader
  						// TODO: consider safer way
  						.replace( /#include\s+<[\w_]*light[\w_]*>/g, '' );
  
  			var defines = {};
  
  			if ( ! /vec3\s+transformed\s*=/.test( originalVertexShader ) &&
  			     ! /#include\s+<begin_vertex>/.test( originalVertexShader ) ) defines.DECLARE_TRANSFORMED = true;
  
  			return new ShaderMaterial( {
  				defines: defines,
  				uniforms: uniforms,
  				vertexShader: vertexShader,
  				fragmentShader: fragmentShader,
  				side: BackSide,
  				//wireframe: true,
  				skinning: false,
  				morphTargets: false,
  				morphNormals: false,
  				fog: false
  			} );
  
  		}
  
  		function getOutlineMaterialFromCache( originalMaterial ) {
  
  			var data = cache[ originalMaterial.uuid ];
  
  			if ( data === undefined ) {
  
  				data = {
  					material: createMaterial( originalMaterial ),
  					used: true,
  					keepAlive: defaultKeepAlive,
  					count: 0
  				};
  
  				cache[ originalMaterial.uuid ] = data;
  
  			}
  
  			data.used = true;
  
  			return data.material;
  
  		}
  
  		function getOutlineMaterial( originalMaterial ) {
  
  			var outlineMaterial = getOutlineMaterialFromCache( originalMaterial );
  
  			originalMaterials[ outlineMaterial.uuid ] = originalMaterial;
  
  			updateOutlineMaterial( outlineMaterial, originalMaterial );
  
  			return outlineMaterial;
  
  		}
  
  		function setOutlineMaterial( object ) {
  
  			if ( object.material === undefined ) return;
  
  			if ( Array.isArray( object.material ) ) {
  
  				for ( var i = 0, il = object.material.length; i < il; i ++ ) {
  
  					object.material[ i ] = getOutlineMaterial( object.material[ i ] );
  
  				}
  
  			} else {
  
  				object.material = getOutlineMaterial( object.material );
  
  			}
  
  			originalOnBeforeRenders[ object.uuid ] = object.onBeforeRender;
  			object.onBeforeRender = onBeforeRender;
  
  		}
  
  		function restoreOriginalMaterial( object ) {
  
  			if ( object.material === undefined ) return;
  
  			if ( Array.isArray( object.material ) ) {
  
  				for ( var i = 0, il = object.material.length; i < il; i ++ ) {
  
  					object.material[ i ] = originalMaterials[ object.material[ i ].uuid ];
  
  				}
  
  			} else {
  
  				object.material = originalMaterials[ object.material.uuid ];
  
  			}
  
  			object.onBeforeRender = originalOnBeforeRenders[ object.uuid ];
  
  		}
  
  		function onBeforeRender( renderer, scene, camera, geometry, material, group ) {
  
  			var originalMaterial = originalMaterials[ material.uuid ];
  
  			// just in case
  			if ( originalMaterial === undefined ) return;
  
  			updateUniforms( material, originalMaterial );
  
  		}
  
  		function updateUniforms( material, originalMaterial ) {
  
  			var outlineParameters = originalMaterial.outlineParameters;
  
  			material.uniforms.outlineAlpha.value = originalMaterial.opacity;
  
  			if ( outlineParameters !== undefined ) {
  
  				if ( outlineParameters.thickness !== undefined ) material.uniforms.outlineThickness.value = outlineParameters.thickness;
  				if ( outlineParameters.color !== undefined ) material.uniforms.outlineColor.value.copy( outlineParameters.color );
  				if ( outlineParameters.alpha !== undefined ) material.uniforms.outlineAlpha.value = outlineParameters.alpha;
  
  			}
  
  		}
  
  		function updateOutlineMaterial( material, originalMaterial ) {
  
  			if ( material.name === 'invisible' ) return;
  
  			var outlineParameters = originalMaterial.outlineParameters;
  
  			material.skinning = originalMaterial.skinning;
  			material.morphTargets = originalMaterial.morphTargets;
  			material.morphNormals = originalMaterial.morphNormals;
  			material.fog = originalMaterial.fog;
  
  			if ( outlineParameters !== undefined ) {
  
  				if ( originalMaterial.visible === false ) {
  
  					material.visible = false;
  
  				} else {
  
  					material.visible = ( outlineParameters.visible !== undefined ) ? outlineParameters.visible : true;
  
  				}
  
  				material.transparent = ( outlineParameters.alpha !== undefined && outlineParameters.alpha < 1.0 ) ? true : originalMaterial.transparent;
  
  				if ( outlineParameters.keepAlive !== undefined ) cache[ originalMaterial.uuid ].keepAlive = outlineParameters.keepAlive;
  
  			} else {
  
  				material.transparent = originalMaterial.transparent;
  				material.visible = originalMaterial.visible;
  
  			}
  
  			if ( originalMaterial.wireframe === true || originalMaterial.depthTest === false ) material.visible = false;
  
  		}
  
  		function cleanupCache() {
  
  			var keys;
  
  			// clear originialMaterials
  			keys = Object.keys( originalMaterials );
  
  			for ( var i = 0, il = keys.length; i < il; i ++ ) {
  
  				originalMaterials[ keys[ i ] ] = undefined;
  
  			}
  
  			// clear originalOnBeforeRenders
  			keys = Object.keys( originalOnBeforeRenders );
  
  			for ( var i = 0, il = keys.length; i < il; i ++ ) {
  
  				originalOnBeforeRenders[ keys[ i ] ] = undefined;
  
  			}
  
  			// remove unused outlineMaterial from cache
  			keys = Object.keys( cache );
  
  			for ( var i = 0, il = keys.length; i < il; i ++ ) {
  
  				var key = keys[ i ];
  
  				if ( cache[ key ].used === false ) {
  
  					cache[ key ].count++;
  
  					if ( cache[ key ].keepAlive === false && cache[ key ].count > removeThresholdCount ) {
  
  						delete cache[ key ];
  
  					}
  
  				} else {
  
  					cache[ key ].used = false;
  					cache[ key ].count = 0;
  
  				}
  
  			}
  
  		}
  
  		this.render = function ( scene, camera, renderTarget, forceClear ) {
  
  			if ( this.enabled === false ) {
  
  				renderer.render( scene, camera, renderTarget, forceClear );
  				return;
  
  			}
  
  			var currentAutoClear = renderer.autoClear;
  			renderer.autoClear = this.autoClear;
  
  			// 1. render normally
  			renderer.render( scene, camera, renderTarget, forceClear );
  
  			// 2. render outline
  			var currentSceneAutoUpdate = scene.autoUpdate;
  			var currentSceneBackground = scene.background;
  			var currentShadowMapEnabled = renderer.shadowMap.enabled;
  
  			scene.autoUpdate = false;
  			scene.background = null;
  			renderer.autoClear = false;
  			renderer.shadowMap.enabled = false;
  
  			scene.traverse( setOutlineMaterial );
  
  			renderer.render( scene, camera, renderTarget );
  
  			scene.traverse( restoreOriginalMaterial );
  
  			cleanupCache();
  
  			scene.autoUpdate = currentSceneAutoUpdate;
  			scene.background = currentSceneBackground;
  			renderer.autoClear = currentAutoClear;
  			renderer.shadowMap.enabled = currentShadowMapEnabled;
  
  		};
  
  		
  		this.autoClear = renderer.autoClear;
  		this.domElement = renderer.domElement;
  		this.shadowMap = renderer.shadowMap;
  
  		this.clear = function ( color, depth, stencil ) {
  
  			renderer.clear( color, depth, stencil );
  
  		};
  
  		this.getPixelRatio = function () {
  
  			return renderer.getPixelRatio();
  
  		};
  
  		this.setPixelRatio = function ( value ) {
  
  			renderer.setPixelRatio( value );
  
  		};
  
  		this.getSize = function () {
  
  			return renderer.getSize();
  
  		};
  
  		this.setSize = function ( width, height, updateStyle ) {
  
  			renderer.setSize( width, height, updateStyle );
  
  		};
  
  		this.setViewport = function ( x, y, width, height ) {
  
  			renderer.setViewport( x, y, width, height );
  
  		};
  
  		this.setScissor = function ( x, y, width, height ) {
  
  			renderer.setScissor( x, y, width, height );
  
  		};
  
  		this.setScissorTest = function ( boolean ) {
  
  			renderer.setScissorTest( boolean );
  
  		};
  
  		this.setRenderTarget = function ( renderTarget ) {
  
  			renderer.setRenderTarget( renderTarget );
  
  		};
  
  	};
  
  	exports.OutlineEffect = OutlineEffect;
  
  	return exports;
  
  }({}));
  